1
28 package demo.layout.hierarchic;
29
30 import demo.view.DemoBase;
31 import y.base.DataMap;
32 import y.base.Edge;
33 import y.base.EdgeCursor;
34 import y.base.EdgeList;
35 import y.base.EdgeMap;
36 import y.base.GraphEvent;
37 import y.base.GraphListener;
38 import y.base.Node;
39 import y.base.NodeCursor;
40 import y.base.NodeList;
41 import y.base.NodeMap;
42 import y.geom.YPoint;
43 import y.layout.NodeLayout;
44 import y.layout.PortConstraint;
45 import y.layout.PortConstraintKeys;
46 import y.layout.hierarchic.GivenLayersLayerer;
47 import y.layout.hierarchic.IncrementalHierarchicLayouter;
48 import y.layout.hierarchic.AsIsLayerer;
49 import y.layout.hierarchic.incremental.IncrementalHintsFactory;
50 import y.layout.hierarchic.incremental.IntValueHolderAdapter;
51 import y.layout.hierarchic.incremental.RoutingStyle;
52 import y.util.Maps;
53 import y.view.Arrow;
54 import y.view.Bend;
55 import y.view.BendCursor;
56 import y.view.BendList;
57 import y.view.BridgeCalculator;
58 import y.view.CreateEdgeMode;
59 import y.view.DefaultGraph2DRenderer;
60 import y.view.Drawable;
61 import y.view.EdgeRealizer;
62 import y.view.EditMode;
63 import y.view.Graph2D;
64 import y.view.Graph2DUndoManager;
65 import y.view.HitInfo;
66 import y.view.HotSpotMode;
67 import y.view.LineType;
68 import y.view.NodeRealizer;
69 import y.view.PopupMode;
70 import y.view.PortAssignmentMoveSelectionMode;
71
72 import javax.swing.AbstractAction;
73 import javax.swing.Action;
74 import javax.swing.JMenu;
75 import javax.swing.JPopupMenu;
76 import javax.swing.JToolBar;
77 import java.awt.Color;
78 import java.awt.Cursor;
79 import java.awt.Graphics2D;
80 import java.awt.Rectangle;
81 import java.awt.Shape;
82 import java.awt.Stroke;
83 import java.awt.EventQueue;
84 import java.awt.event.ActionEvent;
85 import java.awt.geom.Rectangle2D;
86 import java.beans.PropertyChangeListener;
87 import java.net.URL;
88 import java.util.ArrayList;
89 import java.util.HashSet;
90 import java.util.List;
91 import java.util.Locale;
92 import java.util.Set;
93
94
149 public class IncrementalHierarchicLayouterDemo extends DemoBase {
150 private EdgeMap sourcePortMap;
151 private EdgeMap targetPortMap;
152 private LayerDrawable layerDrawable;
153 private NodeMap layerIdMap;
154 private DataMap hintMap;
155
156 private PortAssignmentMoveSelectionMode paMode;
157
158 private IncrementalHierarchicLayouter hierarchicLayouter;
159 private IncrementalHintsFactory hintsFactory;
160 private GivenLayersLayerer gll;
161
162 private boolean loaded;
163 private LayoutAction layoutAction;
164 private Graph2DUndoManager undoManager;
165
166 public IncrementalHierarchicLayouterDemo() {
167 final Graph2D graph = view.getGraph2D();
168
169 EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
171 defaultER.setArrow(Arrow.STANDARD);
172
173 BridgeCalculator bridgeCalculator = new BridgeCalculator();
175 bridgeCalculator.setCrossingMode(BridgeCalculator.CROSSING_MODE_HORIZONTAL_CROSSES_VERTICAL);
176 ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(bridgeCalculator);
177
178 layerIdMap = graph.createNodeMap();
180 sourcePortMap = graph.createEdgeMap();
181 targetPortMap = graph.createEdgeMap();
182 hintMap = Maps.createHashedDataMap();
183
184 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
186 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
187 graph.addDataProvider(GivenLayersLayerer.LAYER_ID_KEY, layerIdMap);
188 graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, hintMap);
189 graph.addDataProvider(IncrementalHierarchicLayouter.LAYER_VALUE_HOLDER_DPKEY,
190 new IntValueHolderAdapter(layerIdMap));
191
192 this.layerDrawable = new LayerDrawable(graph, layerIdMap);
194 view.addBackgroundDrawable(layerDrawable);
195
196 hierarchicLayouter = new IncrementalHierarchicLayouter();
198 hierarchicLayouter.setFixedElementsLayerer(gll = new GivenLayersLayerer());
199 hintsFactory = hierarchicLayouter.createIncrementalHintsFactory();
200 hierarchicLayouter.setComponentLayouterEnabled(false);
201 hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
202
203 hierarchicLayouter.getEdgeLayoutDescriptor().setSourcePortOptimizationEnabled(true);
204 hierarchicLayouter.getEdgeLayoutDescriptor().setTargetPortOptimizationEnabled(true);
205 hierarchicLayouter.getEdgeLayoutDescriptor().setRoutingStyle(new RoutingStyle(RoutingStyle.EDGE_STYLE_OCTILINEAR));
206
207 paMode.setSpc(sourcePortMap);
209 paMode.setTpc(targetPortMap);
210
211 initGraph(graph);
212 }
213
214 protected void initialize() {
215 super.initialize();
216
217 view.getGraph2D().addGraphListener(new GraphListener() {
220 public void onGraphEvent(GraphEvent e) {
221 final byte eventType = e.getType();
222 if(eventType == GraphEvent.POST_EDGE_REMOVAL
223 || eventType == GraphEvent.POST_NODE_REMOVAL) {
224 layoutAction.setEnabled(true);
225 }
226 }
227 });
228 }
229
230 private void initGraph(Graph2D graph) {
231 Node n1 = graph.createNode();
232 layerIdMap.setInt(n1, 0);
233 Node n2 = graph.createNode(100.0, 0.0);
234 layerIdMap.setInt(n2, 1);
235 Node n3 = graph.createNode();
236 layerIdMap.setInt(n3, 2);
237 Node n4 = graph.createNode();
238 layerIdMap.setInt(n4, 2);
239 graph.createEdge(n1, n2);
240 graph.createEdge(n2, n4);
241 graph.createEdge(n1, n3);
242 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
243 Node n = nc.node();
244 graph.getRealizer(n).setLabelText(Integer.toString(n.index() + 1));
245 }
246 calcLayout();
247
248 getUndoManager().resetQueue();
249 }
250
251
254 protected Graph2DUndoManager getUndoManager() {
255 if (undoManager == null) {
256 undoManager = new Graph2DUndoManager(view.getGraph2D()) {
257 public void undo() {
258 super.undo();
259 layerDrawable.updateLayers();
260 }
261
262 public void redo() {
263 super.redo();
264 layerDrawable.updateLayers();
265 }
266 };
267 undoManager.setViewContainer(view);
268 }
269 return undoManager;
270 }
271
272
275 protected Action createPasteAction() {
276 final CustomPasteAction action = new CustomPasteAction(getClipboard().getPasteAction());
277 action.putValue(Action.SMALL_ICON, getIconResource("resource/paste.png"));
278 action.putValue(Action.SHORT_DESCRIPTION, "Paste");
279 return action;
280 }
281
282
285 protected void registerViewActions() {
286 super.registerViewActions();
287 view.getCanvasComponent().getActionMap().put("PASTE", createPasteAction());
288 }
289
290 protected void registerViewModes() {
291 EditMode editMode = new IncrementalEditMode();
292 editMode.setMoveSelectionMode(paMode = new IncrementalMoveSelectionMode());
293 editMode.setPopupMode(new IncrementalPopupMode());
294 editMode.setCreateEdgeMode(new IncrementalEdgeCreateMode());
295 editMode.setHotSpotMode(new IncrementalHotSpotMode());
296 view.addViewMode(editMode);
297 }
298
299 protected JToolBar createToolBar() {
300 JToolBar bar = super.createToolBar();
301 bar.addSeparator();
302 layoutAction = new LayoutAction();
303 bar.add(createActionControl(layoutAction, true));
304 return bar;
305 }
306
307 protected void loadGraph(URL resource) {
308 loaded = true;
309 layoutAction.setEnabled(true);
310 if (layerDrawable != null) {
311 layerDrawable.clearLayers();
312 }
313 super.loadGraph(resource);
314 }
315
316
319 final class LayoutAction extends AbstractAction {
320 LayoutAction() {
321 super("Layout");
322 this.putValue(Action.SMALL_ICON, getIconResource("resource/layout.png"));
323 this.putValue(Action.SHORT_DESCRIPTION, "Apply first layout when a new graph is loaded.");
324 }
325
326 public void actionPerformed(ActionEvent ev) {
327 calcLayout();
328 }
329 }
330
331
334 final class FreshLayoutAction extends AbstractAction {
335 boolean resetPCs;
336
337 FreshLayoutAction(String name, boolean resetPCs) {
338 super(name);
339 this.resetPCs = resetPCs;
340 }
341
342 public void actionPerformed(ActionEvent ev) {
343 if (resetPCs) {
344 for (EdgeCursor ec = view.getGraph2D().edges(); ec.ok(); ec.next()) {
345 sourcePortMap.set(ec.edge(), null);
346 targetPortMap.set(ec.edge(), null);
347 }
348 }
349 byte oldMode = hierarchicLayouter.getLayoutMode();
350 hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
351 try {
352 calcLayout();
353 } finally {
354 hierarchicLayouter.setLayoutMode(oldMode);
355 }
356 }
357 }
358
359
362 final class OptimizeNodesAction extends AbstractAction {
363 private NodeCursor nc;
364 private boolean resetPCs;
365
366 public OptimizeNodesAction(String name, NodeCursor nodes, boolean resetPCs) {
367 super(name);
368 this.nc = nodes;
369 this.resetPCs = resetPCs;
370 }
371
372 public void actionPerformed(ActionEvent ae) {
373 for (nc.toFirst(); nc.ok(); nc.next()) {
374 Node v = nc.node();
375 hintMap.set(v, hintsFactory.createLayerIncrementallyHint(v));
376 if (resetPCs) {
377 for (EdgeCursor ec = v.edges(); ec.ok(); ec.next()) {
378 if (ec.edge().source() == v) {
379 sourcePortMap.set(ec.edge(), null);
380 } else {
381 targetPortMap.set(ec.edge(), null);
382 }
383 }
384 }
385 }
386 calcLayout();
387 for (nc.toFirst(); nc.ok(); nc.next()) {
388 Node v = nc.node();
389 hintMap.set(v, null);
390 }
391 }
392 }
393
394
397 final class FixNodesAction extends AbstractAction {
398 private final NodeCursor nc;
399 private final boolean layer;
400 private final boolean sequence;
401
402 public FixNodesAction(String name, NodeCursor nodes, boolean layer, boolean sequence) {
403 super(name);
404 this.nc = nodes;
405 this.layer = layer;
406 this.sequence = sequence;
407 }
408
409 public void actionPerformed(ActionEvent ae) {
410 for (nc.toFirst(); nc.ok(); nc.next()) {
411 Node v = nc.node();
412 if (layer && sequence) {
413 hintMap.set(v, hintsFactory.createUseExactCoordinatesHint(v));
414 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
415 realizer.setFillColor(Color.red);
416 realizer.repaint();
417 } else if (layer) {
418 hintMap.set(v, hintsFactory.createUseExactLayerCoordinatesHint(v));
419 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
420 realizer.setFillColor(Color.red.darker());
421 realizer.repaint();
422 } else if (sequence) {
423 hintMap.set(v, hintsFactory.createUseExactSequenceCoordinatesHint(v));
424 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
425 realizer.setFillColor(Color.red.darker().darker());
426 realizer.repaint();
427 } else {
428 hintMap.set(v, null);
429 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
430 realizer.setFillColor(view.getGraph2D().getDefaultNodeRealizer().getFillColor());
431 realizer.repaint();
432 }
433 }
434 }
435 }
436
437
440 final class OptimizeEdgesAction extends AbstractAction {
441 private EdgeCursor ec;
442 private boolean resetPCs;
443
444 public OptimizeEdgesAction(String name, EdgeCursor edges, boolean resetPCs) {
445 super(name);
446 this.ec = edges;
447 this.resetPCs = resetPCs;
448 }
449
450 public void actionPerformed(ActionEvent ae) {
451 for (ec.toFirst(); ec.ok(); ec.next()) {
452 final Edge edge = ec.edge();
453 hintMap.set(edge, hintsFactory.createSequenceIncrementallyHint(edge));
454 if (resetPCs) {
455 sourcePortMap.set(edge, null);
456 targetPortMap.set(edge, null);
457 }
458 }
459 calcLayout();
460 for (ec.toFirst(); ec.ok(); ec.next()) {
461 Edge e = ec.edge();
462 hintMap.set(e, null);
463 }
464 }
465 }
466
467
470 static final class LayerDrawable implements Drawable {
471
472 private static final Color[] colors = {new Color(150,200,255,128), new Color(220,240,240,128)};
473
474 private List layers = new ArrayList(20);
475 private Rectangle bounds = new Rectangle(20, 20, 200, 200);
476
477 private Graph2D graph;
478 private NodeMap layerIdMap;
479 private AsIsLayerer ail;
480
481 LayerDrawable(Graph2D graph, NodeMap layerIdMap) {
482 this.graph = graph;
483 this.layerIdMap = layerIdMap;
484 this.ail = new AsIsLayerer();
485 }
486
487 public Rectangle getBounds() {
488 return bounds;
489 }
490
491 public void clearLayers() {
492 layers.clear();
493 }
494
495 public void updateLayers() {
496 final double spacing = 20.0d;
497 layers.clear();
498 updateLayerIds();
499 if (graph.N() < 1) {
500 return;
501 }
502 double minX = Double.MAX_VALUE, maxX = -Double.MAX_VALUE;
503 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
504 final Node node = nc.node();
505 final int layer = layerIdMap.getInt(node);
506 if (layer < 0) {
507 continue;
508 }
509 while (layers.size() - 1 < layer) {
510 layers.add(new Rectangle2D.Double(0.0, 0.0, -1.0, -1.0));
511 }
512 Rectangle2D.Double layerRect = (Rectangle2D.Double) layers.get(layer);
513 final NodeLayout nl = graph.getNodeLayout(node);
514 if (layerRect.width < 0.0) {
515 layerRect.setFrame(nl.getX(), nl.getY(), nl.getWidth(), nl.getHeight());
516 } else {
517 layerRect.add(nl.getX(), nl.getY());
518 layerRect.add(nl.getX() + nl.getWidth(), nl.getY() + nl.getHeight());
519 }
520 minX = Math.min(nl.getX(), minX);
521 maxX = Math.max(nl.getX() + nl.getWidth(), maxX);
522 }
523
524 double minY = Double.MAX_VALUE;
525 double maxY = -Double.MAX_VALUE;
526 for (int i = 0; i < layers.size(); i++) {
527 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(i);
528 rect.x = minX - spacing;
529 rect.width = maxX - minX + spacing * 2.0;
530 if (i == 0) {
531 rect.y -= spacing;
532 rect.height += spacing;
533 minY = rect.y;
534 }
535 if (i == layers.size() - 1) {
536 rect.height += spacing;
537 maxY = rect.height + rect.y;
538 } else if (i < layers.size() - 1) {
539 Rectangle2D.Double nextRect = (Rectangle2D.Double) layers.get(i + 1);
540 final double mid = (rect.getY() + rect.getHeight() + nextRect.getY()) * 0.5d;
541 rect.height += mid - (rect.y + rect.height);
542 final double nextDelta = mid - nextRect.y;
543 nextRect.y += nextDelta;
544 nextRect.height -= nextDelta;
545 }
546 }
547 bounds.setFrame(minX - spacing, minY, maxX - minX + 2.0 * spacing, maxY - minY);
548 graph.updateViews();
549 }
550
551 private void updateLayerIds() {
552 final EdgeList reversedEdges = new EdgeList();
553
554 ail.assignNodeLayer(graph, layerIdMap, reversedEdges);
556
557 for (EdgeCursor ec = reversedEdges.edges(); ec.ok(); ec.next()) {
559 final Edge edge = ec.edge();
560 graph.reverseEdge(edge);
561 }
562 }
563
564 public final int inset = 8;
565
566 public int getLayerId(double x, double y) {
567 if (x < bounds.x - outerInsets || x > bounds.x + bounds.width + outerInsets) {
568 return Integer.MAX_VALUE;
569 }
570 if (y < bounds.y + inset) {
571 return -1;
572 }
573 if (y > bounds.y + bounds.height - inset) {
574 return layers.size();
575 }
576 for (int i = 0; i < layers.size(); i++) {
577 final Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(i);
578 if (y >= rect.y + inset && y <= rect.y + rect.height - inset) {
579 return i;
580 } else if (y < rect.y + inset) {
581 return -(i + 1);
582 }
583 }
584 return Integer.MAX_VALUE;
585 }
586
587 public static final double outerInsets = 40.0;
588
589 public Rectangle2D getLayerBounds(int layer) {
590 if (layer >= 0 && layer < layers.size()) {
591 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(layer);
592 rect = new Rectangle2D.Double(rect.x, rect.y + inset, rect.width, rect.height - (double) (2 * inset));
593 return rect;
594 }
595 if (layer == -1) {
596 return new Rectangle2D.Double(bounds.x, bounds.y - outerInsets, bounds.width, outerInsets + inset);
597 }
598 if (layer >= layers.size() && (layer != Integer.MAX_VALUE)) {
599 return new Rectangle2D.Double(bounds.x, bounds.y + bounds.height - inset, bounds.width, outerInsets);
600 }
601 if (layer < 0) {
602 int beforeLayer = -(layer + 1);
603 if (beforeLayer < layers.size()) {
604 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(beforeLayer);
605 rect = new Rectangle2D.Double(rect.x, rect.y - inset, rect.width, (double) (2 * inset));
606 return rect;
607 }
608 }
609 return new Rectangle2D.Double(bounds.x - 2 * outerInsets, bounds.y - 2 * outerInsets,
610 bounds.width + 4.0 * outerInsets, bounds.height + outerInsets * 4.0);
611 }
612
613 public void paint(Graphics2D g) {
614 for (int i = 0; i < layers.size(); i++) {
615 Color color = colors[i % colors.length];
616 g.setColor(color);
617 g.fill((Shape) layers.get(i));
618 }
619 }
620 }
621
622
625 public void calcLayout() {
626 if (loaded) {
627 hierarchicLayouter.setFixedElementsLayerer(new AsIsLayerer());
628 } else {
629 hierarchicLayouter.setFixedElementsLayerer(gll);
630 }
631 loaded = false;
632 layoutAction.setEnabled(false);
633
634 if (!view.getGraph2D().isEmpty()) {
635 gll.normalize(view.getGraph2D(), layerIdMap, layerIdMap);
636 Cursor oldCursor = view.getViewCursor();
637 try {
638 view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
639 view.applyLayoutAnimated(hierarchicLayouter);
640 } finally {
641 view.setViewCursor(oldCursor);
642 }
643 }
644 layerDrawable.updateLayers();
645 view.updateView();
646 }
647
648
651 final class IncrementalHotSpotMode extends HotSpotMode {
652 public void mousePressedLeft(double x, double y) {
653 final Graph2D graph = getGraph2D();
654 graph.firePreEvent();
656 graph.backupRealizers();
657 super.mousePressedLeft(x, y);
658 }
659
660 public void mouseReleasedLeft(double x, double y) {
661 super.mouseReleasedLeft(x, y);
662 calcLayout();
663 getGraph2D().firePostEvent();
665 }
666 }
667
668
671 final class IncrementalEdgeCreateMode extends CreateEdgeMode {
672 protected Edge createEdge(Graph2D graph, Node startNode, Node targetNode, EdgeRealizer realizer) {
673 graph.firePreEvent();
675 return super.createEdge(graph, startNode, targetNode, realizer);
676 }
677
678 protected void edgeCreated(Edge edge) {
679 super.edgeCreated(edge);
680 EdgeRealizer er = view.getGraph2D().getRealizer(edge);
681 if (er.bendCount() > 0) {
682 parseBend(er.getBend(0));
683 }
684 if (er.bendCount() > 1) {
685 parseBend(er.getBend(er.bendCount() - 1));
686 }
687 if (er.bendCount() == 0) {
688 hintMap.set(edge, hintsFactory.createSequenceIncrementallyHint(edge));
689 }
690 calcLayout();
691 hintMap.set(edge, null);
692
693 getGraph2D().firePostEvent();
695 }
696 }
697
698
701 final class IncrementalEditMode extends EditMode {
702 protected Node createNode(Graph2D graph, double x, double y) {
703 graph.firePreEvent();
705 return super.createNode(graph, x, y);
706 }
707
708 protected void nodeCreated(Node v) {
709 super.nodeCreated(v);
710 final YPoint center = view.getGraph2D().getCenter(v);
711 int layerId = layerDrawable.getLayerId(center.x, center.y);
712 setLayers(new NodeList(v).nodes(), layerId, Integer.MAX_VALUE);
713 if (lastReleaseEvent.isControlDown()) { hintMap.set(v, hintsFactory.createUseExactCoordinatesHint(v));
715 view.getGraph2D().getRealizer(v).setFillColor(Color.red);
716 }
717 calcLayout();
718 getGraph2D().firePostEvent();
720 }
721 }
722
723
726 protected void setLayers(NodeCursor nodes, int newLayer, int previousLayer) {
727 if (!nodes.ok()) {
728 return;
729 }
730 int lesserLayers = 0;
732 int greaterLayers = 0;
733 final Set nodeSet = new HashSet();
734 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
735 nodeSet.add(nodes.node());
736 }
737 if (previousLayer != Integer.MAX_VALUE) {
738 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
739 int pLayer = layerIdMap.getInt(nodes.node());
740 if (pLayer < previousLayer) {
741 lesserLayers = Math.max(lesserLayers, previousLayer - pLayer);
742 }
743 if (pLayer > previousLayer) {
744 greaterLayers = Math.max(greaterLayers, pLayer - previousLayer);
745 }
746 }
747 } else {
748 previousLayer = 0;
749 }
750 final int newLayerCount = lesserLayers + greaterLayers + 1;
751 if (newLayer < 0) {
752 int beforeLayer = -(newLayer + 1);
753 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()) {
754 if (!nodeSet.contains(nc.node())) {
755 int oldLayer = layerIdMap.getInt(nc.node());
756 if (oldLayer >= beforeLayer) {
757 layerIdMap.setInt(nc.node(), oldLayer + newLayerCount);
758 }
759 }
760 }
761 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
762 int oldLayer = layerIdMap.getInt(nodes.node());
763 layerIdMap.setInt(nodes.node(), beforeLayer + lesserLayers + oldLayer - previousLayer);
764 }
765 } else {
766 if (newLayer == Integer.MAX_VALUE) {
767 int maxLayer = -1;
768 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()) {
769 if (!nodeSet.contains(nc.node())) {
770 int layer = layerIdMap.getInt(nc.node());
771 maxLayer = Math.max(layer, maxLayer);
772 }
773 }
774 newLayer = maxLayer + 1;
775 }
776 if (lesserLayers > 0 || greaterLayers > 0) {
777 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()) {
778 if (!nodeSet.contains(nc.node())) {
779 int layer = layerIdMap.getInt(nc.node());
780 if (layer == newLayer) {
781 layerIdMap.setInt(nc.node(), layer + lesserLayers);
782 } else if (layer > newLayer) {
783 layerIdMap.setInt(nc.node(), layer + newLayerCount);
784 }
785 }
786 }
787 }
788 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
789 int oldLayer = layerIdMap.getInt(nodes.node());
790 layerIdMap.setInt(nodes.node(), newLayer + lesserLayers + oldLayer - previousLayer);
791 }
792 }
793 }
794
795
798 public void parseBend(Bend b) {
799 Edge e = b.getEdge();
800 EdgeRealizer er = view.getGraph2D().getRealizer(e);
801 if (b == er.getBend(0)) {
802 YPoint center = view.getGraph2D().getCenter(e.source());
803 sourcePortMap.set(e, getPortConstraint(b.getX() - center.x, b.getY() - center.y));
804 }
805 if (b == er.getBend(er.bendCount() - 1)) {
806 YPoint center = view.getGraph2D().getCenter(e.target());
807 targetPortMap.set(e, getPortConstraint(b.getX() - center.x, b.getY() - center.y));
808 }
809 }
810
811
814 private static PortConstraint getPortConstraint(final double bdx, final double bdy) {
815 if (Math.abs(bdx) > Math.abs(bdy)) {
816 return PortConstraint.create(bdx > 0.0 ? PortConstraint.EAST : PortConstraint.WEST);
817 } else {
818 return PortConstraint.create(bdy > 0.0 ? PortConstraint.SOUTH : PortConstraint.NORTH);
819 }
820 }
821
822
825 final class IncrementalPopupMode extends PopupMode {
826
827 public JPopupMenu getNodePopup(final Node v) {
828 JPopupMenu pm = new JPopupMenu();
829 NodeCursor node = new NodeList(v).nodes();
830 addNodeActions(pm, node);
831 return pm;
832 }
833
834 private void addNodeActions(JPopupMenu pm, NodeCursor node) {
835 pm.add(new OptimizeNodesAction("Optimize Node", node, false));
836 pm.add(new OptimizeNodesAction("Optimize Node and Reset PCs", node, true));
837 JMenu fixNodesMenu = new JMenu("Fix nodes");
838 pm.add(fixNodesMenu);
839 fixNodesMenu.add(new FixNodesAction("Fix Coordinates", node, true, true));
840 fixNodesMenu.add(new FixNodesAction("Fix Layer Coordinates", node, true, false));
841 fixNodesMenu.add(new FixNodesAction("Fix Sequence Coordinates", node, false, true));
842 fixNodesMenu.add(new FixNodesAction("Unfix Coordinates", node, false, false));
843 }
844
845 public JPopupMenu getEdgePopup(final Edge e) {
846 JPopupMenu pm = new JPopupMenu();
847 addEdgeActions(pm, new EdgeList(e).edges());
848 return pm;
849 }
850
851 public JPopupMenu getSelectionPopup(double x, double y) {
852 JPopupMenu pm = new JPopupMenu();
853 final NodeCursor snc = getGraph2D().selectedNodes();
854 if (snc.ok()) {
855 addNodeActions(pm, snc);
856 } else {
857 final EdgeCursor sec = getGraph2D().selectedEdges();
858 if (sec.ok()) {
859 addEdgeActions(pm, sec);
860 } else {
861 return null;
862 }
863 }
864 return pm;
865 }
866
867 private void addEdgeActions(JPopupMenu pm, EdgeCursor sec) {
868 pm.add(new OptimizeEdgesAction("Optimize Edges", sec, false));
869 pm.add(new OptimizeEdgesAction("Optimize Edges and Reset PCs", sec, true));
870 }
871
872 public JPopupMenu getPaperPopup(double x, double y) {
873 if (getGraph2D().isEmpty()) {
874 return null;
875 }
876 JPopupMenu pm = new JPopupMenu();
877 pm.add(new FreshLayoutAction("Fresh Layout", false));
878 pm.add(new FreshLayoutAction("Fresh Layout and Reset PCs", true));
879 if (getGraph2D().E() > 0) {
880 addEdgeActions(pm, getGraph2D().edges());
881 }
882 return pm;
883 }
884 }
885
886
889 final class IncrementalMoveSelectionMode extends PortAssignmentMoveSelectionMode {
890 private boolean firstTime = true;
891 private MoveSelectionDrawable drawable;
892 private NodeList selectedNodes;
893 private BendList selectedBends;
894
895 IncrementalMoveSelectionMode() {
896 super(null, null);
897 }
898
899 protected void selectionMoveStarted(double x, double y) {
900 getGraph2D().firePreEvent();
902 super.selectionMoveStarted(x, y);
903 }
904
905 protected void selectionMovedAction(double dx, double dy, double x, double y) {
906 super.selectionMovedAction(dx, dy, x, y);
907 if (selectedNodes != null) {
908 view.removeBackgroundDrawable(drawable);
909 drawable = null;
910 int newLayer = layerDrawable.getLayerId(x, y);
911 HitInfo hi = this.getLastHitInfo();
912 Node movedNode = hi.getHitNode();
913 int originalLayer = movedNode != null ? layerIdMap.getInt(movedNode) : Integer.MAX_VALUE;
914 if (newLayer != originalLayer) {
915 setLayers(selectedNodes.nodes(), newLayer, originalLayer);
916 }
917 if (newLayer != Integer.MAX_VALUE) {
918 List hints = new ArrayList(128);
919 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()) {
920 for (EdgeCursor edges = nc.node().edges(); edges.ok(); edges.next()) {
921 hints.add(edges.edge());
922 hintMap.set(edges.edge(), hintsFactory.createSequenceIncrementallyHint(edges.edge()));
923 }
924 }
925 calcLayout();
926 for (int i = 0; i < hints.size(); i++) {
927 hintMap.set(hints.get(i), null);
928 }
929 } else {
930 List hints = new ArrayList(128);
931 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()) {
932 hints.add(nc.node());
933 hintMap.set(nc.node(), hintsFactory.createLayerIncrementallyHint(nc.node()));
934 }
935 calcLayout();
936 for (int i = 0; i < hints.size(); i++) {
937 Node node = (Node) hints.get(i);
938 hintMap.set(node, null);
939 }
940 layerDrawable.updateLayers();
941 }
942 selectedNodes = null;
943 } else if (selectedBends != null) {
944 calcLayout();
945 }
946 selectedBends = null;
947 selectedNodes = null;
948 firstTime = true;
949
950 getGraph2D().firePostEvent();
952 }
953
954 protected void selectionOnMove(double dx, double dy, double x, double y) {
955 if (firstTime) {
956 firstTime = false;
957 Graph2D g = getGraph2D();
958 NodeCursor nc = g.selectedNodes();
959 selectedBends = null;
960 selectedNodes = null;
961 if (nc.ok()) {
962 selectedNodes = new NodeList(nc);
963 drawable = new MoveSelectionDrawable();
964 view.addBackgroundDrawable(drawable);
965 }
966 BendCursor bc = g.selectedBends();
967 if (selectedNodes == null && bc.ok()) {
968 selectedBends = new BendList(bc);
969 }
970 }
971 super.selectionOnMove(dx, dy, x, y);
972 if (selectedNodes != null) {
973 int layer = layerDrawable.getLayerId(x, y);
974 drawable.layer = layer;
975 drawable.layerCount = layerDrawable.layers.size();
976 drawable.drawable = layerDrawable.getLayerBounds(layer);
977 }
978 }
979
980 final class MoveSelectionDrawable implements Drawable {
981 Shape drawable;
982 int layer;
983 int layerCount;
984 Color color = Color.red;
985 Color color2 = Color.orange;
986 Color color3 = Color.red.darker();
987
988 public Rectangle getBounds() {
989 return drawable.getBounds();
990 }
991
992 public void paint(Graphics2D g) {
993 Stroke s = g.getStroke();
994 if (layer == Integer.MAX_VALUE) {
995 g.setColor(color3);
996 g.setStroke(LineType.DOTTED_3);
997 g.draw(drawable);
998 } else {
999 if (layer >= 0 && layer < layerCount) {
1000 g.setColor(color);
1001 g.setStroke(LineType.LINE_3);
1002 g.draw(drawable);
1003 } else {
1004 g.setColor(color2);
1005 g.fill(drawable);
1006 }
1007 }
1008 g.setStroke(s);
1009 }
1010 }
1011 }
1012
1013
1016 public static void main(String[] args) {
1017 EventQueue.invokeLater(new Runnable() {
1018 public void run() {
1019 Locale.setDefault(Locale.ENGLISH);
1020 initLnF();
1021 (new IncrementalHierarchicLayouterDemo()).start("Incremental Hierarchic Layouter Demo");
1022 }
1023 });
1024 }
1025
1026
1030 private class CustomPasteAction implements Action {
1031 private final Action pasteAction;
1032
1033 public CustomPasteAction(Action pasteAction) {
1034 this.pasteAction = pasteAction;
1035 }
1036
1037 public boolean isEnabled() {
1038 return pasteAction.isEnabled();
1039 }
1040
1041 public void setEnabled(boolean b) {
1042 pasteAction.setEnabled(b);
1043 }
1044
1045 public void addPropertyChangeListener(PropertyChangeListener listener) {
1046 pasteAction.addPropertyChangeListener(listener);
1047 }
1048
1049 public void removePropertyChangeListener(PropertyChangeListener listener) {
1050 pasteAction.removePropertyChangeListener(listener);
1051 }
1052
1053 public Object getValue(String key) {
1054 return pasteAction.getValue(key);
1055 }
1056
1057 public void putValue(String key, Object value) {
1058 pasteAction.putValue(key, value);
1059 }
1060
1061 public void actionPerformed(ActionEvent e) {
1062 pasteAction.actionPerformed(e);
1063
1064 layoutAction.setEnabled(true);
1066 }
1067 }
1068}
1069