1
14 package demo.view.layout.hierarchic;
15
16 import demo.view.DemoBase;
17 import y.anim.AnimationFactory;
18 import y.anim.AnimationPlayer;
19 import y.base.DataMap;
20 import y.base.Edge;
21 import y.base.EdgeCursor;
22 import y.base.EdgeList;
23 import y.base.EdgeMap;
24 import y.base.Node;
25 import y.base.NodeCursor;
26 import y.base.NodeList;
27 import y.base.NodeMap;
28 import y.geom.YPoint;
29 import y.layout.BufferedLayouter;
30 import y.layout.GraphLayout;
31 import y.layout.NodeLayout;
32 import y.layout.PortConstraint;
33 import y.layout.PortConstraintKeys;
34 import y.layout.hierarchic.GivenLayersLayerer;
35 import y.layout.hierarchic.IncrementalHierarchicLayouter;
36 import y.layout.hierarchic.incremental.IncrementalHintsFactory;
37 import y.layout.hierarchic.incremental.IntValueHolderAdapter;
38 import y.layout.hierarchic.incremental.OldLayererWrapper;
39 import y.util.Maps;
40 import y.view.Arrow;
41 import y.view.Bend;
42 import y.view.BendCursor;
43 import y.view.BendList;
44 import y.view.BridgeCalculator;
45 import y.view.CreateEdgeMode;
46 import y.view.DefaultGraph2DRenderer;
47 import y.view.Drawable;
48 import y.view.EdgeRealizer;
49 import y.view.EditMode;
50 import y.view.Graph2D;
51 import y.view.HitInfo;
52 import y.view.HotSpotMode;
53 import y.view.LayoutMorpher;
54 import y.view.LineType;
55 import y.view.NodeRealizer;
56 import y.view.PopupMode;
57 import y.view.PortAssignmentMoveSelectionMode;
58
59 import javax.swing.AbstractAction;
60 import javax.swing.Action;
61 import javax.swing.ImageIcon;
62 import javax.swing.JMenu;
63 import javax.swing.JPopupMenu;
64 import javax.swing.JToolBar;
65 import java.awt.Color;
66 import java.awt.Cursor;
67 import java.awt.Graphics2D;
68 import java.awt.Rectangle;
69 import java.awt.Shape;
70 import java.awt.Stroke;
71 import java.awt.event.ActionEvent;
72 import java.awt.geom.Rectangle2D;
73 import java.net.URL;
74 import java.util.ArrayList;
75 import java.util.HashSet;
76 import java.util.List;
77 import java.util.Set;
78
79
91 public class IncrementalHierarchicLayouterDemo extends DemoBase
92 {
93 private EdgeMap sourcePortMap;
94 private EdgeMap targetPortMap;
95 private LayerDrawable layerDrawable;
96 private NodeMap layerIdMap;
97 private DataMap hintMap;
98
99 private PortAssignmentMoveSelectionMode paMode;
100
101 private IncrementalHierarchicLayouter hierarchicLayouter;
102 private IncrementalHintsFactory hintsFactory;
103 private GivenLayersLayerer gll;
104
105 public IncrementalHierarchicLayouterDemo()
106 {
107 final Graph2D graph = view.getGraph2D();
108
109 EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
111 defaultER.setArrow(Arrow.STANDARD);
112
113 BridgeCalculator bridgeCalculator = new BridgeCalculator();
115 bridgeCalculator.setCrossingMode( BridgeCalculator.CROSSING_MODE_HORIZONTAL_CROSSES_VERTICAL );
116 ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(bridgeCalculator );
117
118 layerIdMap = graph.createNodeMap();
120 sourcePortMap = graph.createEdgeMap();
121 targetPortMap = graph.createEdgeMap();
122 hintMap = Maps.createHashedDataMap();
123
124 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY,sourcePortMap);
126 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY,targetPortMap);
127 graph.addDataProvider(GivenLayersLayerer.LAYER_ID_KEY, layerIdMap);
128 graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, hintMap);
129 graph.addDataProvider(IncrementalHierarchicLayouter.LAYER_VALUE_HOLDER_DPKEY, new IntValueHolderAdapter(layerIdMap));
130
131 this.layerDrawable = new LayerDrawable(graph, layerIdMap);
133 view.addBackgroundDrawable(layerDrawable);
134
135 hierarchicLayouter = new IncrementalHierarchicLayouter();
137 hierarchicLayouter.setFixedElementsLayerer(new OldLayererWrapper(gll = new GivenLayersLayerer()));
138 hintsFactory = hierarchicLayouter.createIncrementalHintsFactory();
139 hierarchicLayouter.setComponentLayouterEnabled(false);
140 hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
141
142 hierarchicLayouter.getEdgeLayoutDescriptor().setSourcePortOptimizationEnabled(true);
143 hierarchicLayouter.getEdgeLayoutDescriptor().setTargetPortOptimizationEnabled(true);
144 hierarchicLayouter.getEdgeLayoutDescriptor().setOrthogonallyRouted(true);
145
146 paMode.setSpc(sourcePortMap);
148 paMode.setTpc(targetPortMap);
149 }
150
151 protected void registerViewModes() {
152 EditMode editMode = new IncrementalEditMode();
153 editMode.setMoveSelectionMode(paMode = new IncrementalMoveSelectionMode());
154 editMode.setPopupMode(new IncrementalPopupMode());
155 editMode.setCreateEdgeMode(new IncrementalEdgeCreateMode());
156 editMode.setHotSpotMode(new IncrementalHotSpotMode());
157 view.addViewMode( editMode );
158 }
159
160 protected JToolBar createToolBar()
161 {
162 JToolBar bar = super.createToolBar();
163 bar.add(new LayoutAction());
164 return bar;
165 }
166
167
170 final class LayoutAction extends AbstractAction
171 {
172 LayoutAction()
173 {
174 super("Layout");
175 URL imageURL = ClassLoader.getSystemResource("demo/view/resource/Layout16.gif");
176 if (imageURL != null){
177 this.putValue(Action.SMALL_ICON, new ImageIcon(imageURL));
178 }
179 this.putValue( Action.SHORT_DESCRIPTION, "Layout");
180 }
181 public void actionPerformed(ActionEvent ev)
182 {
183 calcLayout();
184 }
185 }
186
187
190 final class FreshLayoutAction extends AbstractAction
191 {
192 boolean resetPCs;
193 FreshLayoutAction(String name, boolean resetPCs)
194 {
195 super(name);
196 this.resetPCs = resetPCs;
197 }
198
199 public void actionPerformed(ActionEvent ev)
200 {
201 if (resetPCs){
202 for (EdgeCursor ec = view.getGraph2D().edges(); ec.ok(); ec.next()){
203 sourcePortMap.set(ec.edge(), null);
204 targetPortMap.set(ec.edge(), null);
205 }
206 }
207 byte oldMode = hierarchicLayouter.getLayoutMode();
208 hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
209 try {
210 calcLayout();
211 } finally {
212 hierarchicLayouter.setLayoutMode(oldMode);
213 }
214 }
215 }
216
219 final class OptimizeNodesAction extends AbstractAction {
220 private NodeCursor nc;
221 private boolean resetPCs;
222 public OptimizeNodesAction(String name, NodeCursor nodes, boolean resetPCs){
223 super(name);
224 this.nc = nodes;
225 this.resetPCs = resetPCs;
226 }
227
228 public void actionPerformed(ActionEvent ae){
229 this.nc.toFirst();
230 for (NodeCursor nc = this.nc; nc.ok(); nc.next()){
231 Node v = nc.node();
232 hintMap.set(v, hintsFactory.createLayerIncrementallyHint(v));
233 if (resetPCs) {
234 for (EdgeCursor ec = v.edges(); ec.ok(); ec.next()){
235 if (ec.edge().source() == v){
236 sourcePortMap.set(ec.edge(), null);
237 } else {
238 targetPortMap.set(ec.edge(), null);
239 }
240 }
241 }
242 }
243 calcLayout();
244 this.nc.toFirst();
245 for (NodeCursor nc = this.nc; nc.ok(); nc.next()){
246 Node v = nc.node();
247 hintMap.set(v, null);
248 }
249 }
250 }
251
252
255 final class FixNodesAction extends AbstractAction {
256 private final NodeCursor nc;
257 private final boolean layer;
258 private final boolean sequence;
259
260 public FixNodesAction(String name, NodeCursor nodes, boolean layer, boolean sequence){
261 super(name);
262 this.nc = nodes;
263 this.layer = layer;
264 this.sequence = sequence;
265 }
266
267 public void actionPerformed(ActionEvent ae){
268 this.nc.toFirst();
269 for (NodeCursor nc = this.nc; nc.ok(); nc.next()){
270 Node v = nc.node();
271 if (layer && sequence) {
272 hintMap.set(v, hintsFactory.createUseExactCoordinatesHint(v));
273 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
274 realizer.setFillColor(Color.red);
275 realizer.repaint();
276 } else if (layer) {
277 hintMap.set(v, hintsFactory.createUseExactLayerCoordinatesHint(v));
278 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
279 realizer.setFillColor(Color.red.darker());
280 realizer.repaint();
281 } else if (sequence) {
282 hintMap.set(v, hintsFactory.createUseExactSequenceCoordinatesHint(v));
283 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
284 realizer.setFillColor(Color.red.darker().darker());
285 realizer.repaint();
286 } else {
287 hintMap.set(v, null);
288 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
289 realizer.setFillColor(view.getGraph2D().getDefaultNodeRealizer().getFillColor());
290 realizer.repaint();
291 }
292 }
293 }
294 }
295
296
299 final class OptimizeEdgesAction extends AbstractAction {
300 private EdgeCursor ec;
301 private boolean resetPCs;
302 public OptimizeEdgesAction(String name, EdgeCursor edges, boolean resetPCs){
303 super(name);
304 this.ec = edges;
305 this.resetPCs = resetPCs;
306 }
307
308 public void actionPerformed(ActionEvent ae){
309 this.ec.toFirst();
310 for (EdgeCursor ec = this.ec; ec.ok(); ec.next()){
311 final Edge edge = ec.edge();
312 hintMap.set(edge, hintsFactory.createSequenceIncrementallyHint(edge));
313 if (resetPCs) {
314 sourcePortMap.set(edge, null);
315 targetPortMap.set(edge, null);
316 }
317 }
318 calcLayout();
319 this.ec.toFirst();
320 for (EdgeCursor ec = this.ec; ec.ok(); ec.next()){
321 Edge e = ec.edge();
322 hintMap.set(e, null);
323 }
324 }
325 }
326
327
330 static final class LayerDrawable implements Drawable {
331
332
333 private List layers = new ArrayList(20);
334 private static final Color[] colors = new Color[] {new Color(255, 255, 150), new Color(255, 255, 100)};
335
336 private Rectangle bounds = new Rectangle(20,20,200,200);
337
338 private Graph2D graph;
339 private NodeMap layerIdMap;
340
341 LayerDrawable(Graph2D graph, NodeMap layerIdMap){
342 this.graph = graph;
343 this.layerIdMap = layerIdMap;
344 }
345
346 public Rectangle getBounds()
347 {
348 return bounds;
349 }
350
351 public void updateLayers(){
352 final double spacing = 20.0d;
353 layers.clear();
354 if (graph.N() < 1) return;
355 double minX = Double.MAX_VALUE, maxX = -Double.MAX_VALUE;
356 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()){
357 final Node node = nc.node();
358 final int layer = layerIdMap.getInt(node);
359 if (layer < 0) continue;
360 while (layers.size() - 1 < layer){
361 layers.add(new Rectangle2D.Double(0,0,-1,-1));
362 }
363 Rectangle2D.Double layerRect = (Rectangle2D.Double) layers.get(layer);
364 final NodeLayout nl = graph.getNodeLayout(node);
365 if (layerRect.width < 0){
366 layerRect.setFrame(nl.getX(), nl.getY(), nl.getWidth(), nl.getHeight());
367 } else {
368 layerRect.add(nl.getX(), nl.getY());
369 layerRect.add(nl.getX() + nl.getWidth(), nl.getY() + nl.getHeight());
370 }
371 minX = Math.min(nl.getX(), minX);
372 maxX = Math.max(nl.getX() + nl.getWidth(), maxX);
373 }
374
375 double minY = Double.MAX_VALUE;
376 double maxY = -Double.MAX_VALUE;
377 for (int i = 0; i < layers.size(); i++){
378 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(i);
379 rect.x = minX - spacing;
380 rect.width = maxX - minX + spacing * 2;
381 if (i == 0){
382 rect.y -= spacing;
383 rect.height += spacing;
384 minY = rect.y;
385 }
386 if (i == layers.size() - 1){
387 rect.height += spacing;
388 maxY = rect.height + rect.y;
389 } else if (i < layers.size() - 1){
390 Rectangle2D.Double nextRect = (Rectangle2D.Double) layers.get(i + 1);
391 final double mid = (rect.getY() + rect.getHeight() + nextRect.getY()) * 0.5d;
392 rect.height += mid - (rect.y + rect.height);
393 final double nextDelta = mid - nextRect.y;
394 nextRect.y += nextDelta;
395 nextRect.height -= nextDelta;
396 }
397 }
398 bounds.setFrame(minX - spacing, minY, maxX - minX + 2 * spacing, maxY - minY);
399 graph.updateViews();
400 }
401
402 public final int inset = 8;
403
404 public int getLayerId(double x, double y){
405 if (x < bounds.x - outerInsets || x > bounds.x + bounds.width + outerInsets){
406 return Integer.MAX_VALUE;
407 }
408 if (y < bounds.y + inset){
409 return -1;
410 }
411 if (y > bounds.y + bounds.height - inset){
412 return layers.size();
413 }
414 for (int i = 0; i < layers.size(); i++){
415 final Rectangle2D.Double rect= (Rectangle2D.Double) layers.get(i);
416 if (y >= rect.y + inset && y <= rect.y + rect.height - inset){
417 return i;
418 } else if (y < rect.y + inset){
419 return -(i+1);
420 }
421 }
422 return Integer.MAX_VALUE;
423 }
424
425 public static final double outerInsets = 40;
426
427 public Rectangle2D getLayerBounds(int layer){
428 if (layer >= 0 && layer < layers.size()){
429 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(layer);
430 rect = new Rectangle2D.Double(rect.x, rect.y + inset, rect.width, rect.height - 2 * inset);
431 return rect;
432 }
433 if (layer == -1){
434 return new Rectangle2D.Double(bounds.x, bounds.y - outerInsets, bounds.width, outerInsets + inset);
435 }
436 if (layer >= layers.size() && (layer != Integer.MAX_VALUE)){
437 return new Rectangle2D.Double(bounds.x, bounds.y + bounds.height - inset, bounds.width, outerInsets);
438 }
439 if (layer < 0){
440 int beforeLayer = -(layer + 1);
441 if (beforeLayer < layers.size()){
442 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(beforeLayer);
443 rect = new Rectangle2D.Double(rect.x, rect.y - inset, rect.width, 2 * inset);
444 return rect;
445 }
446 }
447 return new Rectangle2D.Double(bounds.x - 2 * outerInsets, bounds.y - 2 * outerInsets, bounds.width + 4.0d * outerInsets, bounds.height + outerInsets * 4.0d);
448 }
449
450 public void paint(Graphics2D g)
451 {
452 for (int i = 0; i < layers.size(); i++){
453 Color color = colors[i % colors.length];
454 g.setColor(color);
455 g.fill((Shape) layers.get(i));
456 }
457 }
458 }
459
460
463 public void calcLayout(){
464 if (!view.getGraph2D().isEmpty()){
465 gll.normalize(view.getGraph2D(), layerIdMap, layerIdMap);
466 Cursor oldCursor = view.getCanvasComponent().getCursor();
467 try {
468 view.getCanvasComponent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
469 GraphLayout result = new BufferedLayouter(hierarchicLayouter).calcLayout(view.getGraph2D());
470 LayoutMorpher morpher = new LayoutMorpher(view, result);
471 morpher.setSmoothViewTransform(true);
472 morpher.setPreferredDuration(300);
473 final AnimationPlayer player = new AnimationPlayer();
474 player.addAnimationListener(view);
475 player.setFps(120);
476 player.animate(AnimationFactory.createEasedAnimation(morpher));
477 } finally {
478 view.getCanvasComponent().setCursor(oldCursor);
479 }
480 }
481 layerDrawable.updateLayers();
482 view.updateView();
483 }
484
485
488 final class IncrementalHotSpotMode extends HotSpotMode {
489 public void mouseReleasedLeft(double x, double y)
490 {
491 super.mouseReleasedLeft(x, y);
492 calcLayout();
493 }
494 }
495
496
499 final class IncrementalEdgeCreateMode extends CreateEdgeMode {
500 protected void edgeCreated(Edge edge)
501 {
502 super.edgeCreated(edge);
503 EdgeRealizer er = view.getGraph2D().getRealizer(edge);
504 if (er.bendCount()>0){
505 parseBend(er.getBend(0));
506 }
507 if (er.bendCount()>1){
508 parseBend(er.getBend(er.bendCount()-1));
509 }
510 if (er.bendCount() == 0){
511 hintMap.set(edge, hintsFactory.createSequenceIncrementallyHint(edge));
512 }
513 calcLayout();
514 hintMap.set(edge, null);
515 }
516 }
517
518
521 final class IncrementalEditMode extends EditMode {
522 protected void nodeCreated(Node v)
523 {
524 super.nodeCreated(v);
525 final YPoint center = view.getGraph2D().getCenter(v);
526 int layerId = layerDrawable.getLayerId(center.x, center.y);
527 setLayers(new NodeList(v).nodes(), layerId, Integer.MAX_VALUE);
528 if (lastReleaseEvent.isControlDown()) { hintMap.set(v, hintsFactory.createUseExactCoordinatesHint(v));
530 view.getGraph2D().getRealizer(v).setFillColor(Color.red);
531 }
532 calcLayout();
533 }
534 }
535
536
539 protected void setLayers(NodeCursor nodes, int newLayer, int previousLayer){
540 if (!nodes.ok()) return;
541 int lesserLayers = 0;
543 int greaterLayers = 0;
544 final Set nodeSet = new HashSet();
545 for (nodes.toFirst(); nodes.ok(); nodes.next()){
546 nodeSet.add(nodes.node());
547 }
548 if (previousLayer != Integer.MAX_VALUE){
549 for (nodes.toFirst(); nodes.ok(); nodes.next()){
550 int pLayer = layerIdMap.getInt(nodes.node());
551 if (pLayer < previousLayer){
552 lesserLayers = Math.max(lesserLayers, previousLayer - pLayer);
553 }
554 if (pLayer > previousLayer){
555 greaterLayers = Math.max(greaterLayers, pLayer - previousLayer);
556 }
557 }
558 } else {
559 previousLayer = 0;
560 }
561 final int newLayerCount = lesserLayers + greaterLayers + 1;
562 if (newLayer < 0){
563 int beforeLayer = -(newLayer + 1);
564 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()){
565 if (!nodeSet.contains(nc.node())){
566 int oldLayer = layerIdMap.getInt(nc.node());
567 if (oldLayer >= beforeLayer){
568 layerIdMap.setInt(nc.node(), oldLayer + newLayerCount);
569 }
570 }
571 }
572 for (nodes.toFirst(); nodes.ok(); nodes.next()){
573 int oldLayer = layerIdMap.getInt(nodes.node());
574 layerIdMap.setInt(nodes.node(), beforeLayer + lesserLayers + oldLayer - previousLayer);
575 }
576 } else {
577 if (newLayer == Integer.MAX_VALUE){
578 int maxLayer = -1;
579 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()){
580 if (!nodeSet.contains(nc.node())){
581 int layer = layerIdMap.getInt(nc.node());
582 maxLayer = Math.max(layer, maxLayer);
583 }
584 }
585 newLayer = maxLayer + 1;
586 }
587 if (lesserLayers > 0 || greaterLayers > 0){
588 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()){
589 if (!nodeSet.contains(nc.node())){
590 int layer = layerIdMap.getInt(nc.node());
591 if (layer == newLayer) {
592 layerIdMap.setInt(nc.node(), layer + lesserLayers);
593 } else if (layer > newLayer){
594 layerIdMap.setInt(nc.node(), layer + newLayerCount);
595 }
596 }
597 }
598 }
599 for (nodes.toFirst(); nodes.ok(); nodes.next()){
600 int oldLayer = layerIdMap.getInt(nodes.node());
601 layerIdMap.setInt(nodes.node(), newLayer + lesserLayers + oldLayer - previousLayer);
602 }
603 }
604 }
605
606
609 public void parseBend(Bend b){
610 Edge e = b.getEdge();
611 EdgeRealizer er = view.getGraph2D().getRealizer(e);
612 if (b == er.getBend(0)){
613 YPoint center = view.getGraph2D().getCenter(e.source());
614 sourcePortMap.set(e, getPortConstraint(b.getX() - center.x, b.getY() - center.y));
615 }
616 if (b == er.getBend(er.bendCount()-1)){
617 YPoint center = view.getGraph2D().getCenter(e.target());
618 targetPortMap.set(e, getPortConstraint(b.getX() - center.x, b.getY() - center.y));
619 }
620 }
621
622
625 private static final PortConstraint getPortConstraint(final double bdx, final double bdy){
626 if (Math.abs(bdx) > Math.abs(bdy)){
627 return PortConstraint.create(bdx > 0 ? PortConstraint.EAST : PortConstraint.WEST);
628 } else {
629 return PortConstraint.create(bdy > 0 ? PortConstraint.SOUTH : PortConstraint.NORTH);
630 }
631 }
632
633
636 final class IncrementalPopupMode extends PopupMode {
637
638 public JPopupMenu getNodePopup(final Node v)
639 {
640 JPopupMenu pm = new JPopupMenu();
641 NodeCursor node = new NodeList(v).nodes();
642 addNodeActions(pm, node);
643 return pm;
644 }
645
646 private void addNodeActions(JPopupMenu pm, NodeCursor node) {
647 pm.add(new OptimizeNodesAction("Optimize Node", node, false));
648 pm.add(new OptimizeNodesAction("Optimize Node and Reset PCs", node, true));
649 JMenu fixNodesMenu = new JMenu("Fix nodes");
650 pm.add(fixNodesMenu);
651 fixNodesMenu.add(new FixNodesAction("Fix Coordinates", node, true, true));
652 fixNodesMenu.add(new FixNodesAction("Fix Layer Coordinates", node, true, false));
653 fixNodesMenu.add(new FixNodesAction("Fix Sequence Coordinates", node, false, true));
654 fixNodesMenu.add(new FixNodesAction("Unfix Coordinates", node, false, false));
655 }
656
657 public JPopupMenu getEdgePopup(final Edge e)
658 {
659 JPopupMenu pm = new JPopupMenu();
660 addEdgeActions(pm, new EdgeList(e).edges());
661 return pm;
662 }
663
664 public JPopupMenu getSelectionPopup(double x, double y)
665 {
666 JPopupMenu pm = new JPopupMenu();
667 final NodeCursor snc = getGraph2D().selectedNodes();
668 if (snc.ok()){
669 addNodeActions(pm, snc);
670 } else {
671 final EdgeCursor sec = getGraph2D().selectedEdges();
672 if (sec.ok()){
673 addEdgeActions(pm, sec);
674 } else {
675 return null;
676 }
677 }
678 return pm;
679 }
680
681 private void addEdgeActions(JPopupMenu pm, EdgeCursor sec) {
682 pm.add(new OptimizeEdgesAction("Optimize Edges", sec, false));
683 pm.add(new OptimizeEdgesAction("Optimize Edges and Reset PCs", sec, true));
684 }
685
686 public JPopupMenu getPaperPopup(double x, double y)
687 {
688 if (getGraph2D().isEmpty()) return null;
689 JPopupMenu pm = new JPopupMenu();
690 pm.add(new FreshLayoutAction("Fresh Layout", false));
691 pm.add(new FreshLayoutAction("Fresh Layout and Reset PCs", true));
692 if (getGraph2D().E() > 0){
693 addEdgeActions(pm, getGraph2D().edges());
694 }
695 return pm;
696 }
697 }
698
699
702 final class IncrementalMoveSelectionMode extends PortAssignmentMoveSelectionMode {
703 private boolean firstTime = true;
704 private MoveSelectionDrawable drawable;
705 private NodeList selectedNodes;
706 private BendList selectedBends;
707
708 public IncrementalMoveSelectionMode(){
709 super(null, null);
710 }
711
712 protected void selectionMovedAction(double dx, double dy, double x, double y)
713 {
714 super.selectionMovedAction(dx, dy, x, y);
715 if (selectedNodes != null){
716 view.removeBackgroundDrawable(drawable);
717 drawable = null;
718 int newLayer = layerDrawable.getLayerId(x, y);
719 HitInfo hi = this.getLastHitInfo();
720 Node movedNode = hi.getHitNode();
721 int originalLayer = movedNode != null ? layerIdMap.getInt(movedNode) : Integer.MAX_VALUE;
722 if (newLayer != originalLayer){
723 setLayers(selectedNodes.nodes(), newLayer, originalLayer);
724 }
725 if (newLayer != Integer.MAX_VALUE){
726 List hints = new ArrayList(128);
727 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()){
728 for (EdgeCursor edges = nc.node().edges(); edges.ok(); edges.next()){
729 hints.add(edges.edge());
730 hintMap.set(edges.edge(), hintsFactory.createSequenceIncrementallyHint(edges.edge()));
731 }
732 }
733 calcLayout();
734 for (int i = 0; i < hints.size(); i++){
735 hintMap.set(hints.get(i), null);
736 }
737 } else {
738 List hints = new ArrayList(128);
739 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()){
740 hints.add(nc.node());
741 hintMap.set(nc.node(), hintsFactory.createLayerIncrementallyHint(nc.node()));
742 }
743 calcLayout();
744 for (int i = 0; i < hints.size(); i++){
745 Node node = (Node) hints.get(i);
746 hintMap.set(node, null);
747 }
748 layerDrawable.updateLayers();
749 }
750 selectedNodes = null;
751 } else if (selectedBends != null){
752 calcLayout();
753 }
754 selectedBends = null;
755 selectedNodes = null;
756 firstTime = true;
757 }
758
759 protected void selectionOnMove(double dx, double dy, double x, double y)
760 {
761 if (firstTime){
762 firstTime = false;
763 Graph2D g = getGraph2D();
764 NodeCursor nc = g.selectedNodes();
765 selectedBends = null;
766 selectedNodes = null;
767 if (nc.ok()){
768 selectedNodes = new NodeList(nc);
769 drawable = new MoveSelectionDrawable();
770 view.addBackgroundDrawable(drawable);
771 }
772 BendCursor bc = g.selectedBends();
773 if (selectedNodes == null && bc.ok()){
774 selectedBends = new BendList(bc);
775 }
776 }
777 super.selectionOnMove(dx, dy, x, y);
778 if (selectedNodes != null) {
779 int layer = layerDrawable.getLayerId(x, y);
780 drawable.layer = layer;
781 drawable.layerCount = layerDrawable.layers.size();
782 drawable.drawable = layerDrawable.getLayerBounds(layer);
783 }
784 }
785
786 final class MoveSelectionDrawable implements Drawable {
787 Shape drawable;
788 int layer;
789 int layerCount;
790 Color color = Color.red;
791 Color color2 = Color.orange;
792 Color color3 = Color.red.darker();
793
794 public Rectangle getBounds()
795 {
796 return drawable.getBounds();
797 }
798
799 public void paint(Graphics2D g)
800 {
801 Stroke s = g.getStroke();
802 if (layer == Integer.MAX_VALUE){
803 g.setColor(color3);
804 g.setStroke(LineType.DOTTED_3);
805 g.draw(drawable);
806 } else {
807 if (layer >= 0 && layer < layerCount){
808 g.setColor(color);
809 g.setStroke(LineType.LINE_3);
810 g.draw(drawable);
811 } else {
812 g.setColor(color2);
813 g.fill(drawable);
814 }
815 }
816 g.setStroke(s);
817 }
818 }
819 }
820
821
824 public static void main(String[] args)
825 {
826 initLnF();
827 IncrementalHierarchicLayouterDemo demo = new IncrementalHierarchicLayouterDemo();
828 demo.start("Incremental Hierarchic Layouter Demo");
829 }
830 }
831