1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.tree;
29  
30  import demo.view.DemoBase;
31  import y.algo.Bfs;
32  import y.base.Edge;
33  import y.base.EdgeMap;
34  import y.base.Node;
35  import y.base.NodeCursor;
36  import y.base.NodeList;
37  import y.base.NodeMap;
38  import y.geom.YPoint;
39  import y.layout.PortConstraint;
40  import y.layout.PortConstraintKeys;
41  import y.layout.tree.DefaultNodePlacer;
42  import y.layout.tree.DefaultPortAssignment;
43  import y.layout.tree.GenericTreeLayouter;
44  import y.layout.tree.NodePlacer;
45  import y.util.DataProviderAdapter;
46  import y.view.Arrow;
47  import y.view.CreateChildEdgeMode;
48  import y.view.EdgeRealizer;
49  import y.view.EditMode;
50  import y.view.Graph2D;
51  import y.view.Graph2DSelectionEvent;
52  import y.view.Graph2DSelectionListener;
53  import y.view.HotSpotMode;
54  import y.view.LineType;
55  import y.view.NodeRealizer;
56  import y.view.PolyLineEdgeRealizer;
57  import y.view.PopupMode;
58  import y.view.PortAssignmentMoveSelectionMode;
59  
60  import javax.swing.AbstractAction;
61  import javax.swing.JButton;
62  import javax.swing.JLabel;
63  import javax.swing.JMenu;
64  import javax.swing.JPanel;
65  import javax.swing.JPopupMenu;
66  import javax.swing.JSpinner;
67  import javax.swing.JSplitPane;
68  import javax.swing.SpinnerNumberModel;
69  import javax.swing.event.ChangeEvent;
70  import javax.swing.event.ChangeListener;
71  import java.awt.BorderLayout;
72  import java.awt.Color;
73  import java.awt.Cursor;
74  import java.awt.FlowLayout;
75  import java.awt.EventQueue;
76  import java.awt.event.ActionEvent;
77  import java.util.ArrayList;
78  import java.util.List;
79  import java.util.Locale;
80  
81  /**
82   * This demo shows how GenericTreeLayouter can handle port constraints and multiple 
83   * different NodePlacer instances and implementations at the same time. 
84   * <br>
85   * On another note, it also demonstrates how different ViewModes can be subclassed 
86   * or replaced to achieve a completely different application feel.
87   * <br>
88   * Usage: Use the panel on the left to change the layout settings such as port constraints
89   * or node placers for all nodes at a certain level of the tree simultaneously by pressing
90   * the "Apply" button. The panel in the lower left is a preview for the currently displayed
91   * settings. You can also change the settings for individual nodes by using their context
92   * menus.
93   *
94   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/cls_GenericTreeLayouter" target="_blank">Section Generic Tree Layout</a> in the yFiles for Java Developer's Guide
95   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/mvc_controller#cls_ViewMode" target="_blank">Section ViewMode Workings</a> in the yFiles for Java Developer's Guide
96   */
97  public class IncrementalTreeLayouterDemo extends DemoBase {
98    private static final Color[] layerColors = {Color.red, Color.orange, Color.yellow, Color.cyan, Color.green,
99        Color.blue};
100 
101   private EdgeMap targetPortMap;
102   private NodeMap nodePlacerMap;
103   private  NodeMap portAssignmentMap;
104 
105   private PortAssignmentMoveSelectionMode paMode;
106   private double hDistance = 40.0;
107 
108   private double vDistance = 40.0;
109 
110   private GenericTreeLayouter treeLayouter;
111 
112   private DefaultNodePlacerConfigPanel configPanel;
113 
114   private List layerStyles = new ArrayList();
115   private List layerPortStyles = new ArrayList();
116 
117   {
118     layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
119         DefaultNodePlacer.ALIGNMENT_MEDIAN, 40.0, 40.0));
120     layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT,
121         DefaultNodePlacer.ALIGNMENT_LEADING_OFFSET, 20.0, 40.0));
122     layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
123         DefaultNodePlacer.ALIGNMENT_LEADING_OFFSET, DefaultNodePlacer.ROUTING_FORK_AT_ROOT, 10.0, 20.0));
124     layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
125         DefaultNodePlacer.ALIGNMENT_MEDIAN, 40.0, 40.0));
126   }
127 
128   {
129     layerPortStyles.add(new DefaultPortAssignment(DefaultPortAssignment.MODE_PORT_DISTRIBUTED_SOUTH));
130     layerPortStyles.add(new DefaultPortAssignment(DefaultPortAssignment.MODE_NONE));
131     layerPortStyles.add(new DefaultPortAssignment(DefaultPortAssignment.MODE_NONE));
132     layerPortStyles.add(new DefaultPortAssignment(DefaultPortAssignment.MODE_NONE));
133   }
134 
135   public IncrementalTreeLayouterDemo() {
136     final Graph2D graph = view.getGraph2D();
137     EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
138     defaultER.setArrow(Arrow.STANDARD);
139     ((PolyLineEdgeRealizer) defaultER).setSmoothedBends(true);
140     defaultER.setLineType(LineType.LINE_2);
141 
142     EdgeMap sourcePortMap = graph.createEdgeMap();
143     targetPortMap = graph.createEdgeMap();
144     portAssignmentMap = graph.createNodeMap();
145     nodePlacerMap = graph.createNodeMap();
146     graph.addDataProvider(GenericTreeLayouter.NODE_PLACER_DPKEY, nodePlacerMap);
147     graph.addDataProvider(GenericTreeLayouter.PORT_ASSIGNMENT_DPKEY, portAssignmentMap);
148     graph.addDataProvider(GenericTreeLayouter.CHILD_COMPARATOR_DPKEY, new ChildEdgeComparatorProvider());
149     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
150     graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
151 
152 
153     paMode.setSpc(sourcePortMap);
154     paMode.setTpc(targetPortMap);
155 
156     treeLayouter = new GenericTreeLayouter();
157 
158     configPanel = new DefaultNodePlacerConfigPanel();
159     configPanel.adoptPlacerValues((NodePlacer) layerStyles.get(0));
160     configPanel.adoptPortValues((DefaultPortAssignment) layerPortStyles.get(0));
161 
162     JPanel layerChooserPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
163     layerChooserPanel.add(new JLabel("Layer: "));
164     final JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
165     spinner.addChangeListener(new ChangeListener() {
166       public void stateChanged(ChangeEvent ce) {
167         final int layer = ((Number) spinner.getValue()).intValue() - 1;
168         while (layer >= layerStyles.size()) {
169           layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, 40.0, 40.0));
170           layerPortStyles.add(new DefaultPortAssignment());
171         }
172         NodePlacer placer = (NodePlacer) layerStyles.get(layer);
173         DefaultPortAssignment portAssignment = (DefaultPortAssignment) layerPortStyles.get(layer);
174         configPanel.adoptPlacerValues(placer);
175         configPanel.adoptPortValues(portAssignment);
176       }
177     }
178     );
179     layerChooserPanel.add(spinner);
180 
181     configPanel.addChangeListener(new ChangeListener() {
182       public void stateChanged(ChangeEvent ce) {
183         final int layer = ((Number) spinner.getValue()).intValue() - 1;
184         while (layer >= layerStyles.size()) {
185           layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, 40.0, 40.0));
186           layerPortStyles.add(new DefaultPortAssignment());
187         }
188         layerStyles.set(layer, configPanel.createPlacerCopy());
189         layerPortStyles.set(layer, configPanel.createPortAssignmentCopy());
190       }
191     }
192     );
193 
194     JButton button = new JButton(new AbstractAction("Apply") {
195       public void actionPerformed(ActionEvent ae) {
196         final int layer = ((Number) spinner.getValue()).intValue() - 1;
197         NodePlacer placer = (NodePlacer) layerStyles.get(layer);
198         DefaultPortAssignment portAssignment = (DefaultPortAssignment) layerPortStyles.get(layer);
199         NodeList[] layers = Bfs.getLayers(graph, new NodeList(graph.firstNode()));
200         if (layer < layers.length) {
201           for (NodeCursor nc = layers[layer].nodes(); nc.ok(); nc.next()) {
202             nodePlacerMap.set(nc.node(), placer);
203             portAssignmentMap.set(nc.node(), portAssignment);
204           }
205           calcLayout();
206         }
207       }
208     });
209 
210     graph.addGraph2DSelectionListener(new Graph2DSelectionListener() {
211       public void onGraph2DSelectionEvent(Graph2DSelectionEvent ev) {
212         if (ev.isNodeSelection() && graph.isSelectionSingleton()) {
213           Node n = (Node) ev.getSubject();
214           int depth = 1;
215           while (n.inDegree() > 0) {
216             n = n.firstInEdge().source();
217             depth++;
218           }
219           spinner.setValue(new Integer(depth));
220         }
221       }
222     }
223     );
224     layerChooserPanel.add(button);
225 
226     JPanel rightPanel = new JPanel(new BorderLayout());
227     rightPanel.add(configPanel, BorderLayout.CENTER);
228     rightPanel.add(layerChooserPanel, BorderLayout.NORTH);
229 
230     JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, rightPanel, view);
231     sp.setOneTouchExpandable(true);
232     sp.setContinuousLayout(false);
233     contentPane.add(sp, BorderLayout.CENTER);
234     createSampleGraph(graph);
235   }
236 
237   final class ChildEdgeComparatorProvider extends DataProviderAdapter {
238     public Object get(Object forRootNode) {
239       NodePlacer placer = (NodePlacer) nodePlacerMap.get(forRootNode);
240       if (placer instanceof DefaultNodePlacer) {
241         return ((DefaultNodePlacer) placer).createComparator();
242       }
243       return null;
244     }
245   }
246 
247   private void createSampleGraph(Graph2D graph) {
248     graph.clear();
249     Node root = graph.createNode();
250     graph.getRealizer(root).setFillColor(layerColors[0]);
251     nodePlacerMap.set(root, layerStyles.get(0));
252     portAssignmentMap.set(root, layerPortStyles.get(0));
253     createChildren(graph, root, 4, 1, 2);
254     calcLayout();
255   }
256 
257   private void createChildren(Graph2D graph, Node root, int children, int layer, int layers) {
258     for (int i = 0; i < children; i++) {
259       Node child = graph.createNode();
260       graph.createEdge(root, child);
261       graph.getRealizer(child).setFillColor(layerColors[layer % layerColors.length]);
262       if (layerStyles.size() > layer) {
263         nodePlacerMap.set(child, layerStyles.get(layer));
264         portAssignmentMap.set(child, layerPortStyles.get(layer));
265       }
266       if (layers > 0) {
267         createChildren(graph, child, children, layer + 1, layers - 1);
268       }
269     }
270   }
271 
272   protected boolean isDeletionEnabled() {
273     return false;
274   }
275 
276   protected boolean isClipboardEnabled() {
277     return false;
278   }
279 
280   protected void registerViewModes() {
281     EditMode editMode = new TreeCreateEditMode();
282     view.addViewMode(editMode);
283   }
284 
285 
286   public void calcLayout() {
287     if (!view.getGraph2D().isEmpty()) {
288       Cursor oldCursor = view.getViewCursor();
289       try {
290         view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
291         view.applyLayoutAnimated(treeLayouter);
292       } finally {
293         view.setViewCursor(oldCursor);
294       }
295     }
296   }
297 
298   final class TreeCreateChildEdgeMode extends CreateChildEdgeMode {
299 
300     NodeRealizer activeDummyTargetRealizer;
301 
302     protected boolean acceptSourceNode(Node source, double x, double y) {
303       final boolean accept = super.acceptSourceNode(source, x, y);
304       activeDummyTargetRealizer = createChildNodeRealizer();
305       int depth = 1;
306       for (Node n = source; n.inDegree() > 0; n = n.firstInEdge().source()){
307               depth++;
308       }
309       activeDummyTargetRealizer.setFillColor(layerColors[depth % layerColors.length]);
310       return accept;
311     }
312 
313     protected NodeRealizer createDummyTargetNodeRealizer(double x, double y) {
314       return activeDummyTargetRealizer;
315     }
316 
317     public void mouseReleasedLeft(double x, double y) {
318       // fire event to mark start of child creation for undo/redo
319       getGraph2D().firePreEvent();
320       super.mouseReleasedLeft(x, y);
321     }
322 
323     protected void edgeCreated(Edge e) {
324       int depth = 1;
325       for (Node n = e.source(); n.inDegree() > 0; n = n.firstInEdge().source()) {
326         depth++;
327       }
328       Graph2D g = getGraph2D();
329       g.getRealizer(e.target()).setFillColor(layerColors[depth % layerColors.length]);
330       EdgeRealizer er = g.getRealizer(e);
331       if (nodePlacerMap.get(e.source()) == null) {
332         parseNodePlacement(g, e, er);
333       }
334       if (layerStyles.size() > depth) {
335         nodePlacerMap.set(e.target(), layerStyles.get(depth));
336       }
337       parseTargetPort(g, e, er);
338       g.unselectAll();
339       calcLayout();
340 
341       // fire event to mark start of child creation (including layout) for undo/redo
342       g.firePostEvent();
343     }
344 
345     private void parseNodePlacement(Graph2D g, Edge e, EdgeRealizer er) {
346       YPoint firstPoint = er.bendCount() > 0 ? new YPoint(er.firstBend().getX(), er.firstBend().getY()) :
347           g.getTargetPointAbs(e);
348       NodeRealizer source = g.getRealizer(e.source());
349       double dx = firstPoint.x - source.getCenterX();
350       double dy = firstPoint.y - source.getCenterY();
351       final byte placement;
352       final byte alignment = DefaultNodePlacer.ALIGNMENT_MEDIAN;
353       final byte routing = DefaultNodePlacer.ROUTING_FORK;
354       if (Math.abs(dx) > Math.abs(dy)) {
355         if (dx > 0.0) {
356           placement = DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT;
357         } else {
358           placement = DefaultNodePlacer.PLACEMENT_VERTICAL_TO_LEFT;
359         }
360       } else {
361         if (dy > 0.0) {
362           placement = DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD;
363         } else {
364           placement = DefaultNodePlacer.PLACEMENT_HORIZONTAL_UPWARD;
365         }
366       }
367       nodePlacerMap.set(e.source(), new DefaultNodePlacer(placement, alignment, routing, hDistance, vDistance));
368     }
369 
370     private void parseTargetPort(Graph2D g, Edge e, EdgeRealizer er) {
371       if (er.bendCount() > 0) {
372         YPoint lastPoint = new YPoint(er.lastBend().getX(), er.lastBend().getY());
373         NodeRealizer target = g.getRealizer(e.target());
374         double dx = lastPoint.x - target.getCenterX();
375         double dy = lastPoint.y - target.getCenterY();
376         byte side = PortConstraint.ANY_SIDE;
377         if (Math.abs(dx) > Math.abs(dy)) {
378           if (dx > 0.0) {
379             side = PortConstraint.EAST;
380           } else {
381             side = PortConstraint.WEST;
382           }
383         } else {
384           if (dy > 0.0) {
385             side = PortConstraint.SOUTH;
386           } else {
387             side = PortConstraint.NORTH;
388           }
389         }
390         targetPortMap.set(e, PortConstraint.create(side));
391       }
392     }
393 
394     protected NodeRealizer createChildNodeRealizer() {
395       NodeRealizer retValue;
396       retValue = super.createChildNodeRealizer();
397       retValue.setLabelText("");
398       return retValue;
399     }
400 
401   }
402 
403 
404   final class TreeLayouterPopupMode extends PopupMode {
405     private JPopupMenu nodePlacementMenu;
406 
407     TreeLayouterPopupMode() {
408       nodePlacementMenu = new JPopupMenu();
409       JMenu alignment = new JMenu("Root node Alignment");
410       JMenu placement = new JMenu("Child Placement");
411       JMenu routing = new JMenu("Routing Style");
412 
413       nodePlacementMenu.add(placement);
414       nodePlacementMenu.add(alignment);
415       nodePlacementMenu.add(routing);
416 
417       placement.add(new PlacementAction("Horizontally Downwards", DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD));
418       placement.add(new PlacementAction("Horizontally Upwards", DefaultNodePlacer.PLACEMENT_HORIZONTAL_UPWARD));
419       placement.add(new PlacementAction("Vertically to Left", DefaultNodePlacer.PLACEMENT_VERTICAL_TO_LEFT));
420       placement.add(new PlacementAction("Vertically to right", DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT));
421 
422       alignment.add(new AlignmentAction("Offset Leading", DefaultNodePlacer.ALIGNMENT_LEADING_OFFSET));
423 //      alignment.add(new AlignmentAction("Bus Leading", DefaultNodePlacer.ALIGNMENT_LEADING_ON_BUS));
424       alignment.add(new AlignmentAction("Leading", DefaultNodePlacer.ALIGNMENT_LEADING));
425       alignment.add(new AlignmentAction("Centered", DefaultNodePlacer.ALIGNMENT_CENTER));
426       alignment.add(new AlignmentAction("Median", DefaultNodePlacer.ALIGNMENT_MEDIAN));
427       alignment.add(new AlignmentAction("Trailing", DefaultNodePlacer.ALIGNMENT_TRAILING));
428 //      alignment.add(new AlignmentAction("Bus Trailing", DefaultNodePlacer.ALIGNMENT_TRAILING_ON_BUS));
429       alignment.add(new AlignmentAction("Offset Trailing", DefaultNodePlacer.ALIGNMENT_TRAILING_OFFSET));
430 
431       routing.add(new RoutingAction("Fork", DefaultNodePlacer.ROUTING_FORK));
432       routing.add(new RoutingAction("Fork at Root", DefaultNodePlacer.ROUTING_FORK_AT_ROOT));
433       routing.add(new RoutingAction("Poly Line", DefaultNodePlacer.ROUTING_POLY_LINE));
434     }
435 
436     public JPopupMenu getNodePopup(final Node v) {
437       return nodePlacementMenu;
438     }
439 
440     public JPopupMenu getSelectionPopup(double x, double y) {
441       if (getGraph2D().selectedNodes().ok()) {
442         return nodePlacementMenu;
443       } else {
444         return null;
445       }
446     }
447 
448   }
449 
450   abstract class AssignLayouterAction extends AbstractAction {
451 
452     protected AssignLayouterAction(String name) {
453       super(name);
454     }
455 
456     public void actionPerformed(ActionEvent e) {
457       NodeList selectedNodes = new NodeList(IncrementalTreeLayouterDemo.this.view.getGraph2D().selectedNodes());
458 
459       NodePlacer placer = (NodePlacer) nodePlacerMap.get(selectedNodes.firstNode());
460       placer = getPlacer(placer);
461 
462       DefaultNodePlacer dnp = (DefaultNodePlacer) placer;
463       for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()) {
464         nodePlacerMap.set(nc.node(), new DefaultNodePlacer(
465             dnp.getChildPlacement(),
466             dnp.getRootAlignment(),
467             dnp.getRoutingStyle(),
468             dnp.getHorizontalDistance(),
469             dnp.getVerticalDistance(),
470             dnp.getMinFirstSegmentLength(),
471             dnp.getMinLastSegmentLength(),
472             dnp.getMinSlope(),
473             dnp.getMinSlopeHeight()));
474       }
475       calcLayout();
476     }
477 
478     protected abstract NodePlacer getPlacer(NodePlacer placer);
479   }
480 
481   final class PlacementAction extends AssignLayouterAction {
482 
483     private byte newPlacement;
484 
485     public PlacementAction(String name, byte newPlacement) {
486       super(name);
487       this.newPlacement = newPlacement;
488     }
489 
490     protected NodePlacer getPlacer(NodePlacer placer) {
491       if (placer instanceof DefaultNodePlacer) {
492         placer = (DefaultNodePlacer) ((DefaultNodePlacer) placer).clone();
493         ((DefaultNodePlacer) placer).setChildPlacement(newPlacement);
494       } else {
495         placer = new DefaultNodePlacer(newPlacement, DefaultNodePlacer.ALIGNMENT_MEDIAN, hDistance, vDistance);
496       }
497       return placer;
498     }
499   }
500 
501   final class AlignmentAction extends AssignLayouterAction {
502     private byte newAlignment;
503 
504     public AlignmentAction(String name, byte newAlignment) {
505       super(name);
506       this.newAlignment = newAlignment;
507     }
508 
509     protected NodePlacer getPlacer(NodePlacer placer) {
510       if (placer instanceof DefaultNodePlacer) {
511         placer = (DefaultNodePlacer) ((DefaultNodePlacer) placer).clone();
512         ((DefaultNodePlacer) placer).setRootAlignment(newAlignment);
513       } else {
514         placer = new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, newAlignment, hDistance,
515             vDistance);
516       }
517       return placer;
518     }
519   }
520 
521   final class RoutingAction extends AssignLayouterAction {
522     private byte newRouting;
523 
524     public RoutingAction(String name, byte newRouting) {
525       super(name);
526       this.newRouting = newRouting;
527     }
528 
529     protected NodePlacer getPlacer(NodePlacer placer) {
530       if (placer instanceof DefaultNodePlacer) {
531         placer = (DefaultNodePlacer) ((DefaultNodePlacer) placer).clone();
532         ((DefaultNodePlacer) placer).setRoutingStyle(newRouting);
533       } else {
534         placer = new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
535             DefaultNodePlacer.ALIGNMENT_CENTER, newRouting, hDistance, vDistance);
536       }
537       return placer;
538     }
539   }
540 
541   final class TreeHotSpotMode extends HotSpotMode {
542     public void mouseReleasedLeft(double x, double y) {
543       super.mouseReleasedLeft(x, y);
544       calcLayout();
545     }
546   }
547 
548   final class TreeCreateEditMode extends EditMode {
549     TreeCreateEditMode() {
550       super();
551       setMoveSelectionMode(paMode = new TreePortAssignmentMode());
552       setCreateEdgeMode(new TreeCreateChildEdgeMode());
553       setHotSpotMode(new TreeHotSpotMode());
554       setPopupMode(new TreeLayouterPopupMode());
555     }
556 
557     public boolean doAllowNodeCreation() {
558       return getGraph2D().N() == 0;
559     }
560 
561     protected void nodeCreated(Node v) {
562       super.nodeCreated(v);
563       nodePlacerMap.set(v, configPanel.createPlacerCopy());
564     }
565 
566   }
567 
568   final class TreePortAssignmentMode extends PortAssignmentMoveSelectionMode {
569     TreePortAssignmentMode() {
570       super(null, null);
571     }
572 
573     protected boolean isPortReassignmentAllowed(Edge edge, boolean source) {
574       return !source;
575     }
576 
577 //    protected void portConstraintsUpdated(Edge onEdge)
578 //    {
579 //      calcLayout();
580 //    }
581 
582     protected void selectionMovedAction(double dx, double dy, double x, double y) {
583       super.selectionMovedAction(dx, dy, x, y);
584       calcLayout();
585     }
586 
587   }
588 
589   /**
590    * Launches this demo.
591    */
592   public static void main(String[] args) {
593     EventQueue.invokeLater(new Runnable() {
594       public void run() {
595         Locale.setDefault(Locale.ENGLISH);
596         initLnF();
597         (new IncrementalTreeLayouterDemo()).start("Incremental Tree Layouter Demo");
598       }
599     });
600   }
601 }
602