1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.layout.tree;
15  
16  import demo.view.DemoBase;
17  import y.base.Edge;
18  import y.base.Node;
19  import y.layout.tree.DendrogramPlacer;
20  import y.layout.tree.GenericTreeLayouter;
21  import y.view.Arrow;
22  import y.view.CreateChildEdgeMode;
23  import y.view.EdgeRealizer;
24  import y.view.EditMode;
25  import y.view.Graph2D;
26  import y.view.HotSpotMode;
27  import y.view.LineType;
28  import y.view.NodeRealizer;
29  import y.view.PolyLineEdgeRealizer;
30  import y.view.PortAssignmentMoveSelectionMode;
31  
32  import java.awt.Color;
33  import java.awt.Cursor;
34  import java.awt.EventQueue;
35  import java.util.Locale;
36  
37  /**
38   * This demo shows how to dynamically maintain a tree as a dendrogram, i.e. all subtrees of a single
39   * local root align at their bottom border. It uses {@link GenericTreeLayouter}, {@link DendrogramPlacer}
40   * and {@link CreateChildEdgeMode}.
41   * <br/>
42   * Usage: Add new nodes by dragging an edge from the parent node of the new node. In this demo the
43   * create edge gesture does not need to be completed at a target node. It can end anywhere. The target
44   * node is created as a child node of the source node for the gesture. The target location of the gesture
45   * determines the relative position of the new child node among the children of its parent node. Adding
46   * a node, moving a set of selected nodes or changing their sizes triggers a new layout which restores
47   * the dendrogram style of the tree.
48   */
49  public class DendrogramLayouterDemo extends DemoBase
50  {
51    private static final Color[] layerColors = {Color.red, Color.orange, Color.yellow, Color.cyan, Color.green,
52        Color.blue};
53  
54    private GenericTreeLayouter treeLayouter;
55  
56    public DendrogramLayouterDemo()
57    {
58      final Graph2D graph = view.getGraph2D();
59      EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
60      defaultER.setArrow(Arrow.STANDARD);
61      ((PolyLineEdgeRealizer)defaultER).setSmoothedBends(true);
62      defaultER.setLineType(LineType.LINE_2);
63  
64      treeLayouter = new GenericTreeLayouter();
65  
66      DendrogramPlacer dendrogramPlacer = new DendrogramPlacer();
67      treeLayouter.setDefaultNodePlacer(dendrogramPlacer);
68      treeLayouter.setDefaultChildComparator(dendrogramPlacer.createComparator());
69  
70      createSampleGraph(graph);
71    }
72  
73    private void createSampleGraph(Graph2D graph){
74      graph.clear();
75      Node root = graph.createNode();
76      graph.getRealizer(root).setFillColor(layerColors[0]);
77      createChildren(graph, root, 3, 1, 2);
78      calcLayout();
79    }
80  
81    private void createChildren(Graph2D graph, Node root, int children, int layer, int layers){
82      if (graph.nodeCount() % 3 == 2){
83        // do not create nodes for every subtree
84        return;
85      }
86      for (int i = 0; i < children; i++){
87        Node child = graph.createNode();
88        graph.createEdge(root, child);
89        graph.getRealizer(child).setFillColor(layerColors[layer % layerColors.length]);
90        if (layers > 0){
91          createChildren(graph, child, children, layer+1, layers-1);
92        }
93      }
94    }
95  
96    protected boolean isDeletionEnabled(){
97      return false;
98    }
99  
100   protected void registerViewModes() {
101     EditMode editMode = new TreeCreateEditMode();
102     view.addViewMode( editMode );
103   }
104 
105   public void calcLayout(){
106     if (!view.getGraph2D().isEmpty()){
107       Cursor oldCursor = view.getViewCursor();
108       try {
109         view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
110         view.applyLayoutAnimated(treeLayouter);
111       } finally {
112         view.setViewCursor(oldCursor);
113       }
114     }
115   }
116 
117   final class TreeCreateChildEdgeMode extends CreateChildEdgeMode {
118     protected void edgeCreated(Edge e){
119       Graph2D g = getGraph2D();
120       g.setRealizer(e.target(), activeDummyTargetRealizer);
121       g.unselectAll();
122       calcLayout();
123     }
124 
125     NodeRealizer activeDummyTargetRealizer;
126 
127     protected boolean acceptSourceNode(Node source, double x, double y) {
128       final boolean accept = super.acceptSourceNode(source, x, y);
129       activeDummyTargetRealizer = createChildNodeRealizer();
130       int depth = 1;
131       for (Node n = source; n.inDegree() > 0; n = n.firstInEdge().source()){
132               depth++;
133       }
134       activeDummyTargetRealizer.setFillColor(layerColors[depth % layerColors.length]);
135       return accept;
136     }
137 
138     protected NodeRealizer createDummyTargetNodeRealizer(double x, double y) {
139       return activeDummyTargetRealizer;
140     }
141 
142     protected NodeRealizer createChildNodeRealizer()
143     {
144       NodeRealizer retValue;
145       retValue = super.createChildNodeRealizer();
146       retValue.setLabelText("");
147       return retValue;
148     }
149 
150   }
151 
152   final class TreeHotSpotMode extends HotSpotMode {
153     public void mouseReleasedLeft(double x, double y)
154     {
155       super.mouseReleasedLeft(x, y);
156       calcLayout();
157     }
158   }
159 
160   final class TreeCreateEditMode extends EditMode {
161     TreeCreateEditMode(){
162       super();
163       setMoveSelectionMode(new TreeMoveSelectionMode());
164       setCreateEdgeMode(new TreeCreateChildEdgeMode());
165       setHotSpotMode(new TreeHotSpotMode());
166     }
167 
168     public boolean doAllowNodeCreation()
169     {
170       return getGraph2D().N() == 0;
171     }
172   }
173 
174   final class TreeMoveSelectionMode extends PortAssignmentMoveSelectionMode {
175     TreeMoveSelectionMode(){
176       super(null, null);
177     }
178 
179     protected void selectionMovedAction(double dx, double dy, double x, double y)
180     {
181       super.selectionMovedAction(dx, dy, x, y);
182       calcLayout();
183     }
184   }
185 
186   /**
187    * Launches this demo.
188    */
189   public static void main(String[] args) {
190     EventQueue.invokeLater(new Runnable() {
191       public void run() {
192         Locale.setDefault(Locale.ENGLISH);
193         initLnF();
194         (new DendrogramLayouterDemo()).start("Dendrogram Demo");
195       }
196     });
197   }
198 }
199