1
28 package demo.layout.hierarchic;
29
30 import demo.layout.hierarchic.CellSpanLayoutDemo.CellColorManager;
31 import demo.layout.hierarchic.CellSpanLayoutDemo.Span;
32
33 import demo.view.DemoBase;
34 import y.base.Node;
35 import y.base.NodeCursor;
36 import y.base.NodeList;
37 import y.layout.hierarchic.IncrementalHierarchicLayouter;
38 import y.layout.hierarchic.incremental.EdgeLayoutDescriptor;
39 import y.util.GraphCopier;
40 import y.view.Graph2D;
41 import y.view.Graph2DLayoutExecutor;
42 import y.view.Graph2DView;
43 import y.view.NodeRealizer;
44 import y.view.hierarchy.AutoBoundsFeature;
45 import y.view.hierarchy.HierarchyManager;
46 import y.view.tabular.TableGroupNodeRealizer;
47 import y.view.tabular.TableGroupNodeRealizer.Column;
48 import y.view.tabular.TableGroupNodeRealizer.Row;
49 import y.view.tabular.TableGroupNodeRealizer.Table;
50 import y.view.tabular.TableLayoutConfigurator;
51
52 import java.awt.Color;
53 import java.awt.event.ActionEvent;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.Comparator;
57 import java.util.HashSet;
58 import java.util.Iterator;
59 import javax.swing.AbstractAction;
60 import javax.swing.Action;
61 import javax.swing.JToggleButton;
62
63
70 class CellSpanActionFactory {
71
75 private static final String KEY_DESIGN_VIEW = "CellSpanLayoutDemo.designView";
76
77
78
81 private CellSpanActionFactory() {
82 }
83
84
85
89 static Action newAddBefore(
90 final Graph2DView view, final Table table, final Column col
91 ) {
92 return new AddColumn(view, table, col, true);
93 }
94
95
99 static Action newAddAfter(
100 final Graph2DView view, final Table table, final Column col
101 ) {
102 return new AddColumn(view, table, col, false);
103 }
104
105
108 static Action newRemoveColumn(
109 final Graph2D graph, final Table table, final Column col
110 ) {
111 return new RemoveColumn(graph, table, col);
112 }
113
114
118 static Action newAddBefore(
119 final Graph2DView view, final Table table, final Row row
120 ) {
121 return new AddRow(view, table, row, true);
122 }
123
124
128 static Action newAddAfter(
129 final Graph2DView view, final Table table, final Row row
130 ) {
131 return new AddRow(view, table, row, false);
132 }
133
134
137 static Action newRemoveRow(
138 final Graph2D graph, final Table table, final Row row
139 ) {
140 return new RemoveRow(graph, table, row);
141 }
142
143
150 static Action newDeleteSelection( final Graph2DView view ) {
151 return new CellDeleteSelection(view);
152 }
153
154
163 static Action newSampleAction(
164 final String name, final CellSpanLayoutDemo parent, final String resource
165 ) {
166 return new SampleAction(name, parent, resource);
167 }
168
169
178 static Action newSwitchViewStateAction(
179 final String name, final Graph2DView view, final JToggleButton other
180 ) {
181 return new SwitchViewStateAction(name, view, other);
182 }
183
184
185
188 static void backupRealizer( final Graph2D graph, final Node node ) {
189 graph.backupRealizers((new NodeList(node)).nodes());
190 }
191
192
193
199 static void switchViewState( final Graph2DView view ) {
200 final Object value = view.getClientProperty(KEY_DESIGN_VIEW);
201 if (value instanceof Graph2D) {
202
205 view.putClientProperty(KEY_DESIGN_VIEW, null);
206 view.setGraph2D((Graph2D) value);
207 } else {
208
211 final Graph2D graph = view.getGraph2D();
213
214 final Graph2D target = new Graph2D();
219 target.setDefaultNodeRealizer(graph.getDefaultNodeRealizer());
220 target.setDefaultEdgeRealizer(graph.getDefaultEdgeRealizer());
221 final GraphCopier copier = new GraphCopier(graph.getGraphCopyFactory());
222 copier.copy(graph, target);
223
224 final IncrementalHierarchicLayouter algorithm = new IncrementalHierarchicLayouter();
226 algorithm.setConsiderNodeLabelsEnabled(true);
227 final EdgeLayoutDescriptor eld = algorithm.getEdgeLayoutDescriptor();
228 eld.setMinimumFirstSegmentLength(25);
229 eld.setMinimumLastSegmentLength(25);
230
231 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
233 executor.setTableLayoutConfigurator(new CellLayoutConfigurator());
237 executor.setConfiguringTableNodeRealizers(true);
238 executor.setPortIntersectionCalculatorEnabled(true);
239 executor.doLayout(target, new GroupNodeTransformerStage(algorithm));
242
243 view.putClientProperty(KEY_DESIGN_VIEW, graph);
246 view.setGraph2D(target);
247 }
248
249 view.fitContent();
250 view.updateView();
251 }
252
253
254
257 private static final class SampleAction extends AbstractAction {
258 private final CellSpanLayoutDemo parent;
259 private final String resource;
260
261
269 SampleAction(
270 final String name, final CellSpanLayoutDemo parent, final String resource
271 ) {
272 super(name);
273 this.parent = parent;
274 this.resource = resource;
275 }
276
277
280 public void actionPerformed( final ActionEvent e ) {
281 final Graph2DView view = parent.getView();
282
283 final Object value = view.getClientProperty(KEY_DESIGN_VIEW);
288 final boolean inDiagramMode = value instanceof Graph2D;
289 if (inDiagramMode) {
290 view.putClientProperty(KEY_DESIGN_VIEW, null);
291 view.setGraph2D((Graph2D) value);
292 }
293
294 final Graph2D graph = view.getGraph2D();
295 graph.clear();
296
297 parent.loadGraph(resource);
299
300 if (inDiagramMode) {
302 switchViewState(view);
303 }
304 }
305 }
306
307
311 private static final class SwitchViewStateAction extends AbstractAction {
312 private final JToggleButton other;
313 private final Graph2DView view;
314
315
323 SwitchViewStateAction(
324 final String name, final Graph2DView view, final JToggleButton other
325 ) {
326 super(name);
327 this.other = other;
328 this.view = view;
329 }
330
331
335 public void actionPerformed( final ActionEvent e ) {
336 final JToggleButton src = (JToggleButton) e.getSource();
337 if (src.isSelected()) {
338 src.setEnabled(false);
339 other.setEnabled(true);
340 other.setSelected(false);
341 CellSpanActionFactory.switchViewState(view);
342 }
343 }
344 }
345
346
351 private static final class CellDeleteSelection extends DemoBase.DeleteSelection {
352 CellDeleteSelection( final Graph2DView view ) {
353 super(view);
354
355 setDeletionMask(getDeletionMask() & ~TYPE_TABLE_NODE);
360 }
361
362
368 public void delete( final Graph2DView view ) {
369 final Graph2D graph = view.getGraph2D();
370 final HierarchyManager hm = graph.getHierarchyManager();
371 if (hm == null) {
372 super.delete(view);
373 } else {
374 final NodeList tables = new NodeList();
376 for (NodeCursor nc = hm.getChildren(null); nc.ok(); nc.next()) {
377 final Node node = nc.node();
378 if (hm.isGroupNode(node) &&
379 graph.getRealizer(node) instanceof TableGroupNodeRealizer) {
380 tables.add(node);
381 }
382 }
383 if (tables.isEmpty()) {
384 super.delete(view);
385 } else {
386 graph.firePreEvent();
387 graph.backupRealizers(tables.nodes());
391
392 final NodeList disabled = new NodeList();
395 for (NodeCursor nc = tables.nodes(); nc.ok(); nc.next()) {
396 final Node node = nc.node();
397 final AutoBoundsFeature abf =
398 graph.getRealizer(node).getAutoBoundsFeature();
399 if (abf != null && abf.isAutoBoundsEnabled()) {
400 abf.setAutoBoundsEnabled(false);
401 disabled.add(node);
402 }
403 }
404
405 try {
406 super.delete(view);
407 } finally {
408
409 if (!disabled.isEmpty()) {
413 for (NodeCursor nc = disabled.nodes(); nc.ok(); nc.next()) {
414 graph.getRealizer(nc.node())
415 .getAutoBoundsFeature()
416 .setAutoBoundsEnabled(true);
417 }
418 }
419 }
420
421 graph.firePostEvent();
422 }
423 }
424 }
425 }
426
427
430 private static final class AddColumn extends AbstractAction {
431 private final Graph2DView view;
432 private final Table table;
433 private final Column column;
434 private final boolean before;
435
436
439 AddColumn(
440 final Graph2DView view,
441 final Table table, final Column column,
442 final boolean before
443 ) {
444 super("Add Column " + (before ? "Before" : "After"));
445 this.view = view;
446 this.table = table;
447 this.column = column;
448 this.before = before;
449 }
450
451
454 public void actionPerformed( final ActionEvent e ) {
455 final TableGroupNodeRealizer tgnr = table.getRealizer();
456
457 backupRealizer(view.getGraph2D(), tgnr.getNode());
459
460
461 final double oldWidth = tgnr.getWidth();
462
463 final int newIdx = column.getIndex() + (before ? 0 : 1);
464 final Column newCol = table.addColumn(newIdx);
465
466 final double dx = tgnr.getWidth() - oldWidth;
467 if (before && dx > 0) {
470 final AutoBoundsFeature abf = tgnr.getAutoBoundsFeature();
474 final boolean oldEnabled = abf != null && abf.isAutoBoundsEnabled();
475 if (oldEnabled) {
476 abf.setAutoBoundsEnabled(false);
477 }
478 try {
479 tgnr.setLocation(tgnr.getX() - dx, tgnr.getY());
480 } finally {
481 if (oldEnabled) {
482 abf.setAutoBoundsEnabled(true);
483 }
484 }
485 }
486
487
488 final CellColorManager manager = CellColorManager.getInstance(table);
493 manager.shift(newCol, true);
494
495 final int nextIdx = before ? newIdx - 1 : newIdx + 1;
498 if (-1 < nextIdx && nextIdx < table.columnCount()) {
499 final Column next = table.getColumn(nextIdx);
500 for (int i = 0, n = table.rowCount(); i < n; ++i) {
501 final Row row = table.getRow(i);
502 final Color color = manager.getCellColor(column, row);
503 if (color != null && color.equals(manager.getCellColor(next, row))) {
504 manager.setCellColor(newCol, row, color);
505 }
506 }
507 }
508
509 view.updateWorldRect();
511
512 view.getGraph2D().updateViews();
514 }
515 }
516
517
520 private static final class RemoveColumn extends AbstractAction {
521 private final Graph2D graph;
522 private final Table table;
523 private final Column column;
524
525
528 RemoveColumn( final Graph2D graph, final Table table, final Column column ) {
529 super("Remove Column");
530 this.graph = graph;
531 this.table = table;
532 this.column = column;
533 }
534
535
538 public void actionPerformed( final ActionEvent e ) {
539 backupRealizer(graph, table.getRealizer().getNode());
541
542 CellColorManager.getInstance(table).shift(column, false);
547
548 column.remove();
549
550 graph.updateViews();
552 }
553 }
554
555
558 private static final class AddRow extends AbstractAction {
559 private final Graph2DView view;
560 private final Table table;
561 private final Row row;
562 private final boolean before;
563
564
567 AddRow(
568 final Graph2DView view,
569 final Table table, final Row row,
570 final boolean before
571 ) {
572 super("Add Row " + (before ? "Before" : "After"));
573 this.view = view;
574 this.table = table;
575 this.row = row;
576 this.before = before;
577 }
578
579
582 public void actionPerformed( final ActionEvent e ) {
583 final TableGroupNodeRealizer tgnr = table.getRealizer();
584
585 backupRealizer(view.getGraph2D(), tgnr.getNode());
587
588
589 final double oldHeight = tgnr.getHeight();
590
591 final int newIdx = row.getIndex() + (before ? 0 : 1);
592 final Row newRow = table.addRow(newIdx);
593
594 final double dy = tgnr.getHeight() - oldHeight;
595 if (before && dy > 0) {
598 final AutoBoundsFeature abf = tgnr.getAutoBoundsFeature();
602 final boolean oldEnabled = abf != null && abf.isAutoBoundsEnabled();
603 if (oldEnabled) {
604 abf.setAutoBoundsEnabled(false);
605 }
606 try {
607 tgnr.setLocation(tgnr.getX(), tgnr.getY() - dy);
608 } finally {
609 if (oldEnabled) {
610 abf.setAutoBoundsEnabled(true);
611 }
612 }
613 }
614
615 final CellColorManager manager = CellColorManager.getInstance(table);
620 manager.shift(newRow, true);
621
622 final int nextIdx = before ? newIdx - 1 : newIdx + 1;
625 if (-1 < nextIdx && nextIdx < table.rowCount()) {
626 final Row next = table.getRow(nextIdx);
627 for (int i = 0, n = table.columnCount(); i < n; ++i) {
628 final Column col = table.getColumn(i);
629 final Color color = manager.getCellColor(col, row);
630 if (color != null && color.equals(manager.getCellColor(col, next))) {
631 manager.setCellColor(col, newRow, color);
632 }
633 }
634 }
635
636 view.updateWorldRect();
638
639 view.getGraph2D().updateViews();
641 }
642 }
643
644
647 private static final class RemoveRow extends AbstractAction {
648 private final Graph2D graph;
649 private final Table table;
650 private final Row row;
651
652
655 RemoveRow( final Graph2D graph, final Table table, final Row row ) {
656 super("Remove Row");
657 this.graph = graph;
658 this.table = table;
659 this.row = row;
660 }
661
662
665 public void actionPerformed( final ActionEvent e ) {
666 backupRealizer(graph, table.getRealizer().getNode());
668
669 CellColorManager.getInstance(table).shift(row, false);
674
675 row.remove();
676
677 graph.updateViews();
679 }
680 }
681
682
683
686 private static final class CellLayoutConfigurator extends TableLayoutConfigurator {
687
697 public void prepareAll( final Graph2D graph ) {
698 prepareCellSpans(graph);
699 super.prepareAll(graph);
700 }
701
702
708 void prepareCellSpans( final Graph2D graph ) {
709 final HierarchyManager hm = graph.getHierarchyManager();
710 if (hm != null) {
711 for (NodeCursor nc = hm.getChildren(null); nc.ok(); nc.next()) {
712 final Node node = nc.node();
713 if (hm.isGroupNode(node)) {
714 final NodeRealizer nr = graph.getRealizer(node);
715 if (nr instanceof TableGroupNodeRealizer) {
716 prepareTable((TableGroupNodeRealizer) nr);
717 }
718 }
719 }
720 }
721 }
722
723
728 void prepareTable( final TableGroupNodeRealizer tgnr ) {
729 final AutoBoundsFeature abf = tgnr.getAutoBoundsFeature();
733 final boolean oldEnabled = abf != null && abf.isAutoBoundsEnabled();
734 if (oldEnabled) {
735 abf.setAutoBoundsEnabled(false);
736 }
737 try {
738 prepareTableImpl(tgnr);
739 } finally {
740 if (oldEnabled) {
741 abf.setAutoBoundsEnabled(true);
742 }
743 }
744 }
745
746
754 private void prepareTableImpl( final TableGroupNodeRealizer tgnr ) {
755 final Node node = tgnr.getNode();
756 final Graph2D graph = (Graph2D) node.getGraph();
757 final HierarchyManager hm = graph.getHierarchyManager();
758
759 final Table table = tgnr.getTable();
760
761 final CellColorManager manager = CellColorManager.getInstance(table);
762 final HashSet used = new HashSet();
763 used.add(null);
764 for (int i = 0, n = table.columnCount(); i < n; ++i) {
765 final Column column = table.getColumn(i);
766 for (int j = 0, m = table.rowCount(); j < m; ++j) {
767 final Row row = table.getRow(j);
768 final Color color = manager.getCellColor(column, row);
769 if (used.add(color)) {
770 final Span span = Span.find(table, column, row, color);
771
772 final double eps = 2;
778 final double minX = table.getColumn(span.minCol).calculateBounds().getX() + eps;
779 final double maxX = table.getColumn(span.maxCol).calculateBounds().getMaxX() - eps;
780 final double minY = table.getRow(span.minRow).calculateBounds().getY() + eps;
781 final double maxY = table.getRow(span.maxRow).calculateBounds().getMaxY() - eps;
782
783 final Node group = hm.createGroupNode(node);
784
785 final NodeRealizer nr = graph.getRealizer(group);
786 nr.setFrame(minX, minY, maxX - minX, maxY - minY);
787 nr.setFillColor(color);
788 for (int l = nr.labelCount() - 1; l > -1; --l) {
789 nr.removeLabel(l);
790 }
791 }
792 }
793 }
794 }
795
796
797
807 public void restoreAll( final Graph2D graph ) {
808 super.restoreAll(graph);
809 restoreCellSpans(graph);
810 }
811
812
819 void restoreCellSpans( final Graph2D graph ) {
820 final HierarchyManager hm = graph.getHierarchyManager();
821 if (hm != null) {
822 final NodeList tables = new NodeList();
823
824 for (NodeCursor nc = hm.getChildren(null); nc.ok(); nc.next()) {
828 final Node node = nc.node();
829 if (hm.isGroupNode(node) &&
830 graph.getRealizer(node) instanceof TableGroupNodeRealizer) {
831 restoreCellSpanGroups(graph, hm, node);
832 tables.add(node);
833 }
834 }
835
836 for (NodeCursor nc = tables.nodes(); nc.ok(); nc.next()) {
841 graph.removeNode(nc.node());
842 }
843 }
844 }
845
846
854 void restoreCellSpanGroups(
855 final Graph2D graph, final HierarchyManager hm, final Node node
856 ) {
857 final NodeList groups = new NodeList();
862 final ArrayList others = new ArrayList();
863 for (NodeCursor nc = hm.getChildren(node); nc.ok(); nc.next()) {
864 final Node child = nc.node();
865 if (hm.isGroupNode(child)) {
866 groups.add(child);
867 } else {
868 others.add(child);
869 }
870 }
871
872 Collections.sort(others, new NodeCenterOrder(graph));
874
875 for (NodeCursor nc = groups.nodes(); nc.ok(); nc.next()) {
887 final Node group = nc.node();
888 final NodeRealizer nr = graph.getRealizer(group);
889 final double minX = nr.getX();
890 final double maxX = minX + nr.getWidth();
891 final double minY = nr.getY();
892 final double maxY = minY + nr.getHeight();
893
894 final AutoBoundsFeature abf = nr.getAutoBoundsFeature();
898 final boolean oldEnabled = abf != null && abf.isAutoBoundsEnabled();
899 if (oldEnabled) {
900 abf.setAutoBoundsEnabled(false);
901 }
902
903 boolean active = false;
914 for (Iterator it = others.iterator(); it.hasNext(); ) {
915 final Node child = (Node) it.next();
916 final double cx = graph.getCenterX(child);
917 if (minX < cx && cx < maxX) {
918 active = true;
919 final double cy = graph.getCenterY(child);
920 if (minY < cy && cy < maxY) {
921 hm.setParentNode(child, group);
922 }
923 } else if (active) {
924 break;
927 }
928 }
929
930 if (oldEnabled) {
931 abf.setAutoBoundsEnabled(true);
932 }
933 }
934 }
935
936
939 private static class NodeCenterOrder implements Comparator {
940 private final Graph2D graph;
941
942
945 NodeCenterOrder( final Graph2D graph ) {
946 this.graph = graph;
947 }
948
949
952 public int compare( final Object o1, final Object o2 ) {
953 final double cx1 = graph.getCenterX((Node) o1);
954 final double cx2 = graph.getCenterX((Node) o2);
955 final int order = Double.compare(cx1, cx2);
956 if (order == 0) {
957 final double cy1 = graph.getCenterY((Node) o1);
958 final double cy2 = graph.getCenterY((Node) o2);
959 return Double.compare(cy1, cy2);
960 } else {
961 return order;
962 }
963 }
964 }
965 }
966 }
967