1
14 package demo.layout.hierarchic;
15
16 import demo.view.DemoBase;
17 import y.base.DataMap;
18 import y.base.Edge;
19 import y.base.EdgeCursor;
20 import y.base.EdgeList;
21 import y.base.EdgeMap;
22 import y.base.GraphEvent;
23 import y.base.GraphListener;
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.NodeLayout;
30 import y.layout.PortConstraint;
31 import y.layout.PortConstraintKeys;
32 import y.layout.hierarchic.GivenLayersLayerer;
33 import y.layout.hierarchic.IncrementalHierarchicLayouter;
34 import y.layout.hierarchic.AsIsLayerer;
35 import y.layout.hierarchic.incremental.IncrementalHintsFactory;
36 import y.layout.hierarchic.incremental.IntValueHolderAdapter;
37 import y.util.Maps;
38 import y.view.Arrow;
39 import y.view.Bend;
40 import y.view.BendCursor;
41 import y.view.BendList;
42 import y.view.BridgeCalculator;
43 import y.view.CreateEdgeMode;
44 import y.view.DefaultGraph2DRenderer;
45 import y.view.Drawable;
46 import y.view.EdgeRealizer;
47 import y.view.EditMode;
48 import y.view.Graph2D;
49 import y.view.HitInfo;
50 import y.view.HotSpotMode;
51 import y.view.LineType;
52 import y.view.NodeRealizer;
53 import y.view.PopupMode;
54 import y.view.PortAssignmentMoveSelectionMode;
55
56 import javax.swing.AbstractAction;
57 import javax.swing.Action;
58 import javax.swing.JMenu;
59 import javax.swing.JPopupMenu;
60 import javax.swing.JToolBar;
61 import java.awt.Color;
62 import java.awt.Cursor;
63 import java.awt.Graphics2D;
64 import java.awt.Rectangle;
65 import java.awt.Shape;
66 import java.awt.Stroke;
67 import java.awt.EventQueue;
68 import java.awt.event.ActionEvent;
69 import java.awt.geom.Rectangle2D;
70 import java.net.URL;
71 import java.util.ArrayList;
72 import java.util.HashSet;
73 import java.util.List;
74 import java.util.Locale;
75 import java.util.Set;
76
77
132 public class IncrementalHierarchicLayouterDemo extends DemoBase {
133 private EdgeMap sourcePortMap;
134 private EdgeMap targetPortMap;
135 private LayerDrawable layerDrawable;
136 private NodeMap layerIdMap;
137 private DataMap hintMap;
138
139 private PortAssignmentMoveSelectionMode paMode;
140
141 private IncrementalHierarchicLayouter hierarchicLayouter;
142 private IncrementalHintsFactory hintsFactory;
143 private GivenLayersLayerer gll;
144
145 private boolean loaded;
146 private LayoutAction layoutAction;
147
148 public IncrementalHierarchicLayouterDemo() {
149 final Graph2D graph = view.getGraph2D();
150
151 EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
153 defaultER.setArrow(Arrow.STANDARD);
154
155 BridgeCalculator bridgeCalculator = new BridgeCalculator();
157 bridgeCalculator.setCrossingMode(BridgeCalculator.CROSSING_MODE_HORIZONTAL_CROSSES_VERTICAL);
158 ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(bridgeCalculator);
159
160 layerIdMap = graph.createNodeMap();
162 sourcePortMap = graph.createEdgeMap();
163 targetPortMap = graph.createEdgeMap();
164 hintMap = Maps.createHashedDataMap();
165
166 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
168 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
169 graph.addDataProvider(GivenLayersLayerer.LAYER_ID_KEY, layerIdMap);
170 graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, hintMap);
171 graph.addDataProvider(IncrementalHierarchicLayouter.LAYER_VALUE_HOLDER_DPKEY,
172 new IntValueHolderAdapter(layerIdMap));
173
174 this.layerDrawable = new LayerDrawable(graph, layerIdMap);
176 view.addBackgroundDrawable(layerDrawable);
177
178 hierarchicLayouter = new IncrementalHierarchicLayouter();
180 hierarchicLayouter.setFixedElementsLayerer(gll = new GivenLayersLayerer());
181 hintsFactory = hierarchicLayouter.createIncrementalHintsFactory();
182 hierarchicLayouter.setComponentLayouterEnabled(false);
183 hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
184
185 hierarchicLayouter.getEdgeLayoutDescriptor().setSourcePortOptimizationEnabled(true);
186 hierarchicLayouter.getEdgeLayoutDescriptor().setTargetPortOptimizationEnabled(true);
187 hierarchicLayouter.setOrthogonallyRouted(true);
188
189 paMode.setSpc(sourcePortMap);
191 paMode.setTpc(targetPortMap);
192
193 initGraph(graph);
194 }
195
196 protected void initialize() {
197 super.initialize();
198
199 view.getGraph2D().addGraphListener(new GraphListener() {
202 public void onGraphEvent(GraphEvent e) {
203 final byte eventType = e.getType();
204 if(eventType == GraphEvent.POST_EDGE_REMOVAL
205 || eventType == GraphEvent.POST_NODE_REMOVAL) {
206 layoutAction.setEnabled(true);
207 }
208 }
209 });
210 }
211
212 private void initGraph(Graph2D graph) {
213 Node n1 = graph.createNode();
214 layerIdMap.setInt(n1, 0);
215 Node n2 = graph.createNode(100.0, 0.0);
216 layerIdMap.setInt(n2, 1);
217 Node n3 = graph.createNode();
218 layerIdMap.setInt(n3, 2);
219 Node n4 = graph.createNode();
220 layerIdMap.setInt(n4, 2);
221 graph.createEdge(n1, n2);
222 graph.createEdge(n2, n4);
223 graph.createEdge(n1, n3);
224 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
225 Node n = nc.node();
226 graph.getRealizer(n).setLabelText(Integer.toString(n.index() + 1));
227 }
228 calcLayout();
229 }
230
231 protected void registerViewModes() {
232 EditMode editMode = new IncrementalEditMode();
233 editMode.setMoveSelectionMode(paMode = new IncrementalMoveSelectionMode());
234 editMode.setPopupMode(new IncrementalPopupMode());
235 editMode.setCreateEdgeMode(new IncrementalEdgeCreateMode());
236 editMode.setHotSpotMode(new IncrementalHotSpotMode());
237 view.addViewMode(editMode);
238 }
239
240 protected JToolBar createToolBar() {
241 JToolBar bar = super.createToolBar();
242 bar.addSeparator();
243 layoutAction = new LayoutAction();
244 bar.add(createActionControl(layoutAction, true));
245 return bar;
246 }
247
248 protected void loadGraph(URL resource) {
249 loaded = true;
250 layoutAction.setEnabled(true);
251 if (layerDrawable != null) {
252 layerDrawable.clearLayers();
253 }
254 super.loadGraph(resource);
255 }
256
257
260 final class LayoutAction extends AbstractAction {
261 LayoutAction() {
262 super("Layout");
263 this.putValue(Action.SMALL_ICON, getIconResource("resource/layout.png"));
264 this.putValue(Action.SHORT_DESCRIPTION, "Apply first layout when a new graph is loaded.");
265 }
266
267 public void actionPerformed(ActionEvent ev) {
268 calcLayout();
269 }
270 }
271
272
275 final class FreshLayoutAction extends AbstractAction {
276 boolean resetPCs;
277
278 FreshLayoutAction(String name, boolean resetPCs) {
279 super(name);
280 this.resetPCs = resetPCs;
281 }
282
283 public void actionPerformed(ActionEvent ev) {
284 if (resetPCs) {
285 for (EdgeCursor ec = view.getGraph2D().edges(); ec.ok(); ec.next()) {
286 sourcePortMap.set(ec.edge(), null);
287 targetPortMap.set(ec.edge(), null);
288 }
289 }
290 byte oldMode = hierarchicLayouter.getLayoutMode();
291 hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
292 try {
293 calcLayout();
294 } finally {
295 hierarchicLayouter.setLayoutMode(oldMode);
296 }
297 }
298 }
299
300
303 final class OptimizeNodesAction extends AbstractAction {
304 private NodeCursor nc;
305 private boolean resetPCs;
306
307 public OptimizeNodesAction(String name, NodeCursor nodes, boolean resetPCs) {
308 super(name);
309 this.nc = nodes;
310 this.resetPCs = resetPCs;
311 }
312
313 public void actionPerformed(ActionEvent ae) {
314 for (nc.toFirst(); nc.ok(); nc.next()) {
315 Node v = nc.node();
316 hintMap.set(v, hintsFactory.createLayerIncrementallyHint(v));
317 if (resetPCs) {
318 for (EdgeCursor ec = v.edges(); ec.ok(); ec.next()) {
319 if (ec.edge().source() == v) {
320 sourcePortMap.set(ec.edge(), null);
321 } else {
322 targetPortMap.set(ec.edge(), null);
323 }
324 }
325 }
326 }
327 calcLayout();
328 for (nc.toFirst(); nc.ok(); nc.next()) {
329 Node v = nc.node();
330 hintMap.set(v, null);
331 }
332 }
333 }
334
335
338 final class FixNodesAction extends AbstractAction {
339 private final NodeCursor nc;
340 private final boolean layer;
341 private final boolean sequence;
342
343 public FixNodesAction(String name, NodeCursor nodes, boolean layer, boolean sequence) {
344 super(name);
345 this.nc = nodes;
346 this.layer = layer;
347 this.sequence = sequence;
348 }
349
350 public void actionPerformed(ActionEvent ae) {
351 for (nc.toFirst(); nc.ok(); nc.next()) {
352 Node v = nc.node();
353 if (layer && sequence) {
354 hintMap.set(v, hintsFactory.createUseExactCoordinatesHint(v));
355 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
356 realizer.setFillColor(Color.red);
357 realizer.repaint();
358 } else if (layer) {
359 hintMap.set(v, hintsFactory.createUseExactLayerCoordinatesHint(v));
360 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
361 realizer.setFillColor(Color.red.darker());
362 realizer.repaint();
363 } else if (sequence) {
364 hintMap.set(v, hintsFactory.createUseExactSequenceCoordinatesHint(v));
365 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
366 realizer.setFillColor(Color.red.darker().darker());
367 realizer.repaint();
368 } else {
369 hintMap.set(v, null);
370 NodeRealizer realizer = view.getGraph2D().getRealizer(v);
371 realizer.setFillColor(view.getGraph2D().getDefaultNodeRealizer().getFillColor());
372 realizer.repaint();
373 }
374 }
375 }
376 }
377
378
381 final class OptimizeEdgesAction extends AbstractAction {
382 private EdgeCursor ec;
383 private boolean resetPCs;
384
385 public OptimizeEdgesAction(String name, EdgeCursor edges, boolean resetPCs) {
386 super(name);
387 this.ec = edges;
388 this.resetPCs = resetPCs;
389 }
390
391 public void actionPerformed(ActionEvent ae) {
392 for (ec.toFirst(); ec.ok(); ec.next()) {
393 final Edge edge = ec.edge();
394 hintMap.set(edge, hintsFactory.createSequenceIncrementallyHint(edge));
395 if (resetPCs) {
396 sourcePortMap.set(edge, null);
397 targetPortMap.set(edge, null);
398 }
399 }
400 calcLayout();
401 for (ec.toFirst(); ec.ok(); ec.next()) {
402 Edge e = ec.edge();
403 hintMap.set(e, null);
404 }
405 }
406 }
407
408
411 static final class LayerDrawable implements Drawable {
412
413 private static final Color[] colors = {new Color(150,200,255,128), new Color(220,240,240,128)};
414
415 private List layers = new ArrayList(20);
416 private Rectangle bounds = new Rectangle(20, 20, 200, 200);
417
418 private Graph2D graph;
419 private NodeMap layerIdMap;
420
421 LayerDrawable(Graph2D graph, NodeMap layerIdMap) {
422 this.graph = graph;
423 this.layerIdMap = layerIdMap;
424 }
425
426 public Rectangle getBounds() {
427 return bounds;
428 }
429
430 public void clearLayers() {
431 layers.clear();
432 }
433
434 public void updateLayers() {
435 final double spacing = 20.0d;
436 layers.clear();
437 if (graph.N() < 1) {
438 return;
439 }
440 double minX = Double.MAX_VALUE, maxX = -Double.MAX_VALUE;
441 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
442 final Node node = nc.node();
443 final int layer = layerIdMap.getInt(node);
444 if (layer < 0) {
445 continue;
446 }
447 while (layers.size() - 1 < layer) {
448 layers.add(new Rectangle2D.Double(0.0, 0.0, -1.0, -1.0));
449 }
450 Rectangle2D.Double layerRect = (Rectangle2D.Double) layers.get(layer);
451 final NodeLayout nl = graph.getNodeLayout(node);
452 if (layerRect.width < 0.0) {
453 layerRect.setFrame(nl.getX(), nl.getY(), nl.getWidth(), nl.getHeight());
454 } else {
455 layerRect.add(nl.getX(), nl.getY());
456 layerRect.add(nl.getX() + nl.getWidth(), nl.getY() + nl.getHeight());
457 }
458 minX = Math.min(nl.getX(), minX);
459 maxX = Math.max(nl.getX() + nl.getWidth(), maxX);
460 }
461
462 double minY = Double.MAX_VALUE;
463 double maxY = -Double.MAX_VALUE;
464 for (int i = 0; i < layers.size(); i++) {
465 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(i);
466 rect.x = minX - spacing;
467 rect.width = maxX - minX + spacing * 2.0;
468 if (i == 0) {
469 rect.y -= spacing;
470 rect.height += spacing;
471 minY = rect.y;
472 }
473 if (i == layers.size() - 1) {
474 rect.height += spacing;
475 maxY = rect.height + rect.y;
476 } else if (i < layers.size() - 1) {
477 Rectangle2D.Double nextRect = (Rectangle2D.Double) layers.get(i + 1);
478 final double mid = (rect.getY() + rect.getHeight() + nextRect.getY()) * 0.5d;
479 rect.height += mid - (rect.y + rect.height);
480 final double nextDelta = mid - nextRect.y;
481 nextRect.y += nextDelta;
482 nextRect.height -= nextDelta;
483 }
484 }
485 bounds.setFrame(minX - spacing, minY, maxX - minX + 2.0 * spacing, maxY - minY);
486 graph.updateViews();
487 }
488
489 public final int inset = 8;
490
491 public int getLayerId(double x, double y) {
492 if (x < bounds.x - outerInsets || x > bounds.x + bounds.width + outerInsets) {
493 return Integer.MAX_VALUE;
494 }
495 if (y < bounds.y + inset) {
496 return -1;
497 }
498 if (y > bounds.y + bounds.height - inset) {
499 return layers.size();
500 }
501 for (int i = 0; i < layers.size(); i++) {
502 final Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(i);
503 if (y >= rect.y + inset && y <= rect.y + rect.height - inset) {
504 return i;
505 } else if (y < rect.y + inset) {
506 return -(i + 1);
507 }
508 }
509 return Integer.MAX_VALUE;
510 }
511
512 public static final double outerInsets = 40.0;
513
514 public Rectangle2D getLayerBounds(int layer) {
515 if (layer >= 0 && layer < layers.size()) {
516 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(layer);
517 rect = new Rectangle2D.Double(rect.x, rect.y + inset, rect.width, rect.height - (double) (2 * inset));
518 return rect;
519 }
520 if (layer == -1) {
521 return new Rectangle2D.Double(bounds.x, bounds.y - outerInsets, bounds.width, outerInsets + inset);
522 }
523 if (layer >= layers.size() && (layer != Integer.MAX_VALUE)) {
524 return new Rectangle2D.Double(bounds.x, bounds.y + bounds.height - inset, bounds.width, outerInsets);
525 }
526 if (layer < 0) {
527 int beforeLayer = -(layer + 1);
528 if (beforeLayer < layers.size()) {
529 Rectangle2D.Double rect = (Rectangle2D.Double) layers.get(beforeLayer);
530 rect = new Rectangle2D.Double(rect.x, rect.y - inset, rect.width, (double) (2 * inset));
531 return rect;
532 }
533 }
534 return new Rectangle2D.Double(bounds.x - 2 * outerInsets, bounds.y - 2 * outerInsets,
535 bounds.width + 4.0 * outerInsets, bounds.height + outerInsets * 4.0);
536 }
537
538 public void paint(Graphics2D g) {
539 for (int i = 0; i < layers.size(); i++) {
540 Color color = colors[i % colors.length];
541 g.setColor(color);
542 g.fill((Shape) layers.get(i));
543 }
544 }
545 }
546
547
550 public void calcLayout() {
551 if (loaded) {
552 hierarchicLayouter.setFixedElementsLayerer(new AsIsLayerer());
553 } else {
554 hierarchicLayouter.setFixedElementsLayerer(gll);
555 }
556 loaded = false;
557 layoutAction.setEnabled(false);
558
559 if (!view.getGraph2D().isEmpty()) {
560 gll.normalize(view.getGraph2D(), layerIdMap, layerIdMap);
561 Cursor oldCursor = view.getViewCursor();
562 try {
563 view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
564 view.applyLayoutAnimated(hierarchicLayouter);
565 } finally {
566 view.setViewCursor(oldCursor);
567 }
568 }
569 layerDrawable.updateLayers();
570 view.updateView();
571 }
572
573
576 final class IncrementalHotSpotMode extends HotSpotMode {
577 public void mouseReleasedLeft(double x, double y) {
578 super.mouseReleasedLeft(x, y);
579 calcLayout();
580 }
581 }
582
583
586 final class IncrementalEdgeCreateMode extends CreateEdgeMode {
587 protected void edgeCreated(Edge edge) {
588 super.edgeCreated(edge);
589 EdgeRealizer er = view.getGraph2D().getRealizer(edge);
590 if (er.bendCount() > 0) {
591 parseBend(er.getBend(0));
592 }
593 if (er.bendCount() > 1) {
594 parseBend(er.getBend(er.bendCount() - 1));
595 }
596 if (er.bendCount() == 0) {
597 hintMap.set(edge, hintsFactory.createSequenceIncrementallyHint(edge));
598 }
599 calcLayout();
600 hintMap.set(edge, null);
601 }
602 }
603
604
607 final class IncrementalEditMode extends EditMode {
608 protected void nodeCreated(Node v) {
609 super.nodeCreated(v);
610 final YPoint center = view.getGraph2D().getCenter(v);
611 int layerId = layerDrawable.getLayerId(center.x, center.y);
612 setLayers(new NodeList(v).nodes(), layerId, Integer.MAX_VALUE);
613 if (lastReleaseEvent.isControlDown()) { hintMap.set(v, hintsFactory.createUseExactCoordinatesHint(v));
615 view.getGraph2D().getRealizer(v).setFillColor(Color.red);
616 }
617 calcLayout();
618 }
619 }
620
621
624 protected void setLayers(NodeCursor nodes, int newLayer, int previousLayer) {
625 if (!nodes.ok()) {
626 return;
627 }
628 int lesserLayers = 0;
630 int greaterLayers = 0;
631 final Set nodeSet = new HashSet();
632 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
633 nodeSet.add(nodes.node());
634 }
635 if (previousLayer != Integer.MAX_VALUE) {
636 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
637 int pLayer = layerIdMap.getInt(nodes.node());
638 if (pLayer < previousLayer) {
639 lesserLayers = Math.max(lesserLayers, previousLayer - pLayer);
640 }
641 if (pLayer > previousLayer) {
642 greaterLayers = Math.max(greaterLayers, pLayer - previousLayer);
643 }
644 }
645 } else {
646 previousLayer = 0;
647 }
648 final int newLayerCount = lesserLayers + greaterLayers + 1;
649 if (newLayer < 0) {
650 int beforeLayer = -(newLayer + 1);
651 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()) {
652 if (!nodeSet.contains(nc.node())) {
653 int oldLayer = layerIdMap.getInt(nc.node());
654 if (oldLayer >= beforeLayer) {
655 layerIdMap.setInt(nc.node(), oldLayer + newLayerCount);
656 }
657 }
658 }
659 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
660 int oldLayer = layerIdMap.getInt(nodes.node());
661 layerIdMap.setInt(nodes.node(), beforeLayer + lesserLayers + oldLayer - previousLayer);
662 }
663 } else {
664 if (newLayer == Integer.MAX_VALUE) {
665 int maxLayer = -1;
666 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()) {
667 if (!nodeSet.contains(nc.node())) {
668 int layer = layerIdMap.getInt(nc.node());
669 maxLayer = Math.max(layer, maxLayer);
670 }
671 }
672 newLayer = maxLayer + 1;
673 }
674 if (lesserLayers > 0 || greaterLayers > 0) {
675 for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()) {
676 if (!nodeSet.contains(nc.node())) {
677 int layer = layerIdMap.getInt(nc.node());
678 if (layer == newLayer) {
679 layerIdMap.setInt(nc.node(), layer + lesserLayers);
680 } else if (layer > newLayer) {
681 layerIdMap.setInt(nc.node(), layer + newLayerCount);
682 }
683 }
684 }
685 }
686 for (nodes.toFirst(); nodes.ok(); nodes.next()) {
687 int oldLayer = layerIdMap.getInt(nodes.node());
688 layerIdMap.setInt(nodes.node(), newLayer + lesserLayers + oldLayer - previousLayer);
689 }
690 }
691 }
692
693
696 public void parseBend(Bend b) {
697 Edge e = b.getEdge();
698 EdgeRealizer er = view.getGraph2D().getRealizer(e);
699 if (b == er.getBend(0)) {
700 YPoint center = view.getGraph2D().getCenter(e.source());
701 sourcePortMap.set(e, getPortConstraint(b.getX() - center.x, b.getY() - center.y));
702 }
703 if (b == er.getBend(er.bendCount() - 1)) {
704 YPoint center = view.getGraph2D().getCenter(e.target());
705 targetPortMap.set(e, getPortConstraint(b.getX() - center.x, b.getY() - center.y));
706 }
707 }
708
709
712 private static PortConstraint getPortConstraint(final double bdx, final double bdy) {
713 if (Math.abs(bdx) > Math.abs(bdy)) {
714 return PortConstraint.create(bdx > 0.0 ? PortConstraint.EAST : PortConstraint.WEST);
715 } else {
716 return PortConstraint.create(bdy > 0.0 ? PortConstraint.SOUTH : PortConstraint.NORTH);
717 }
718 }
719
720
723 final class IncrementalPopupMode extends PopupMode {
724
725 public JPopupMenu getNodePopup(final Node v) {
726 JPopupMenu pm = new JPopupMenu();
727 NodeCursor node = new NodeList(v).nodes();
728 addNodeActions(pm, node);
729 return pm;
730 }
731
732 private void addNodeActions(JPopupMenu pm, NodeCursor node) {
733 pm.add(new OptimizeNodesAction("Optimize Node", node, false));
734 pm.add(new OptimizeNodesAction("Optimize Node and Reset PCs", node, true));
735 JMenu fixNodesMenu = new JMenu("Fix nodes");
736 pm.add(fixNodesMenu);
737 fixNodesMenu.add(new FixNodesAction("Fix Coordinates", node, true, true));
738 fixNodesMenu.add(new FixNodesAction("Fix Layer Coordinates", node, true, false));
739 fixNodesMenu.add(new FixNodesAction("Fix Sequence Coordinates", node, false, true));
740 fixNodesMenu.add(new FixNodesAction("Unfix Coordinates", node, false, false));
741 }
742
743 public JPopupMenu getEdgePopup(final Edge e) {
744 JPopupMenu pm = new JPopupMenu();
745 addEdgeActions(pm, new EdgeList(e).edges());
746 return pm;
747 }
748
749 public JPopupMenu getSelectionPopup(double x, double y) {
750 JPopupMenu pm = new JPopupMenu();
751 final NodeCursor snc = getGraph2D().selectedNodes();
752 if (snc.ok()) {
753 addNodeActions(pm, snc);
754 } else {
755 final EdgeCursor sec = getGraph2D().selectedEdges();
756 if (sec.ok()) {
757 addEdgeActions(pm, sec);
758 } else {
759 return null;
760 }
761 }
762 return pm;
763 }
764
765 private void addEdgeActions(JPopupMenu pm, EdgeCursor sec) {
766 pm.add(new OptimizeEdgesAction("Optimize Edges", sec, false));
767 pm.add(new OptimizeEdgesAction("Optimize Edges and Reset PCs", sec, true));
768 }
769
770 public JPopupMenu getPaperPopup(double x, double y) {
771 if (getGraph2D().isEmpty()) {
772 return null;
773 }
774 JPopupMenu pm = new JPopupMenu();
775 pm.add(new FreshLayoutAction("Fresh Layout", false));
776 pm.add(new FreshLayoutAction("Fresh Layout and Reset PCs", true));
777 if (getGraph2D().E() > 0) {
778 addEdgeActions(pm, getGraph2D().edges());
779 }
780 return pm;
781 }
782 }
783
784
787 final class IncrementalMoveSelectionMode extends PortAssignmentMoveSelectionMode {
788 private boolean firstTime = true;
789 private MoveSelectionDrawable drawable;
790 private NodeList selectedNodes;
791 private BendList selectedBends;
792
793 IncrementalMoveSelectionMode() {
794 super(null, null);
795 }
796
797 protected void selectionMovedAction(double dx, double dy, double x, double y) {
798 super.selectionMovedAction(dx, dy, x, y);
799 if (selectedNodes != null) {
800 view.removeBackgroundDrawable(drawable);
801 drawable = null;
802 int newLayer = layerDrawable.getLayerId(x, y);
803 HitInfo hi = this.getLastHitInfo();
804 Node movedNode = hi.getHitNode();
805 int originalLayer = movedNode != null ? layerIdMap.getInt(movedNode) : Integer.MAX_VALUE;
806 if (newLayer != originalLayer) {
807 setLayers(selectedNodes.nodes(), newLayer, originalLayer);
808 }
809 if (newLayer != Integer.MAX_VALUE) {
810 List hints = new ArrayList(128);
811 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()) {
812 for (EdgeCursor edges = nc.node().edges(); edges.ok(); edges.next()) {
813 hints.add(edges.edge());
814 hintMap.set(edges.edge(), hintsFactory.createSequenceIncrementallyHint(edges.edge()));
815 }
816 }
817 calcLayout();
818 for (int i = 0; i < hints.size(); i++) {
819 hintMap.set(hints.get(i), null);
820 }
821 } else {
822 List hints = new ArrayList(128);
823 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()) {
824 hints.add(nc.node());
825 hintMap.set(nc.node(), hintsFactory.createLayerIncrementallyHint(nc.node()));
826 }
827 calcLayout();
828 for (int i = 0; i < hints.size(); i++) {
829 Node node = (Node) hints.get(i);
830 hintMap.set(node, null);
831 }
832 layerDrawable.updateLayers();
833 }
834 selectedNodes = null;
835 } else if (selectedBends != null) {
836 calcLayout();
837 }
838 selectedBends = null;
839 selectedNodes = null;
840 firstTime = true;
841 }
842
843 protected void selectionOnMove(double dx, double dy, double x, double y) {
844 if (firstTime) {
845 firstTime = false;
846 Graph2D g = getGraph2D();
847 NodeCursor nc = g.selectedNodes();
848 selectedBends = null;
849 selectedNodes = null;
850 if (nc.ok()) {
851 selectedNodes = new NodeList(nc);
852 drawable = new MoveSelectionDrawable();
853 view.addBackgroundDrawable(drawable);
854 }
855 BendCursor bc = g.selectedBends();
856 if (selectedNodes == null && bc.ok()) {
857 selectedBends = new BendList(bc);
858 }
859 }
860 super.selectionOnMove(dx, dy, x, y);
861 if (selectedNodes != null) {
862 int layer = layerDrawable.getLayerId(x, y);
863 drawable.layer = layer;
864 drawable.layerCount = layerDrawable.layers.size();
865 drawable.drawable = layerDrawable.getLayerBounds(layer);
866 }
867 }
868
869 final class MoveSelectionDrawable implements Drawable {
870 Shape drawable;
871 int layer;
872 int layerCount;
873 Color color = Color.red;
874 Color color2 = Color.orange;
875 Color color3 = Color.red.darker();
876
877 public Rectangle getBounds() {
878 return drawable.getBounds();
879 }
880
881 public void paint(Graphics2D g) {
882 Stroke s = g.getStroke();
883 if (layer == Integer.MAX_VALUE) {
884 g.setColor(color3);
885 g.setStroke(LineType.DOTTED_3);
886 g.draw(drawable);
887 } else {
888 if (layer >= 0 && layer < layerCount) {
889 g.setColor(color);
890 g.setStroke(LineType.LINE_3);
891 g.draw(drawable);
892 } else {
893 g.setColor(color2);
894 g.fill(drawable);
895 }
896 }
897 g.setStroke(s);
898 }
899 }
900 }
901
902
905 public static void main(String[] args) {
906 EventQueue.invokeLater(new Runnable() {
907 public void run() {
908 Locale.setDefault(Locale.ENGLISH);
909 initLnF();
910 (new IncrementalHierarchicLayouterDemo()).start("Incremental Hierarchic Layouter Demo");
911 }
912 });
913 }
914 }
915