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.base.Edge;
32  import y.base.Node;
33  import y.layout.tree.DendrogramPlacer;
34  import y.layout.tree.GenericTreeLayouter;
35  import y.view.Arrow;
36  import y.view.CreateChildEdgeMode;
37  import y.view.EdgeRealizer;
38  import y.view.EditMode;
39  import y.view.Graph2D;
40  import y.view.HotSpotMode;
41  import y.view.LineType;
42  import y.view.NodeRealizer;
43  import y.view.PolyLineEdgeRealizer;
44  import y.view.PortAssignmentMoveSelectionMode;
45  
46  import java.awt.Color;
47  import java.awt.Cursor;
48  import java.awt.EventQueue;
49  import java.util.Locale;
50  
51  /**
52   * This demo shows how to dynamically maintain a tree as a dendrogram, i.e. all subtrees of a single
53   * local root align at their bottom border. It uses {@link GenericTreeLayouter}, {@link DendrogramPlacer}
54   * and {@link CreateChildEdgeMode}.
55   * <br/>
56   * Usage: Add new nodes by dragging an edge from the parent node of the new node. In this demo the
57   * create edge gesture does not need to be completed at a target node. It can end anywhere. The target
58   * node is created as a child node of the source node for the gesture. The target location of the gesture
59   * determines the relative position of the new child node among the children of its parent node. Adding
60   * a node, moving a set of selected nodes or changing their sizes triggers a new layout which restores
61   * the dendrogram style of the tree.
62   */
63  public class DendrogramLayouterDemo extends DemoBase
64  {
65    private static final Color[] layerColors = {Color.red, Color.orange, Color.yellow, Color.cyan, Color.green,
66        Color.blue};
67  
68    private GenericTreeLayouter treeLayouter;
69  
70    public DendrogramLayouterDemo()
71    {
72      final Graph2D graph = view.getGraph2D();
73      EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
74      defaultER.setArrow(Arrow.STANDARD);
75      ((PolyLineEdgeRealizer)defaultER).setSmoothedBends(true);
76      defaultER.setLineType(LineType.LINE_2);
77  
78      treeLayouter = new GenericTreeLayouter();
79  
80      DendrogramPlacer dendrogramPlacer = new DendrogramPlacer();
81      treeLayouter.setDefaultNodePlacer(dendrogramPlacer);
82      treeLayouter.setDefaultChildComparator(dendrogramPlacer.createComparator());
83  
84      createSampleGraph(graph);
85    }
86  
87    private void createSampleGraph(Graph2D graph){
88      graph.clear();
89      Node root = graph.createNode();
90      graph.getRealizer(root).setFillColor(layerColors[0]);
91      createChildren(graph, root, 3, 1, 2);
92      calcLayout();
93      getUndoManager().resetQueue();
94    }
95  
96    private void createChildren(Graph2D graph, Node root, int children, int layer, int layers){
97      if (graph.nodeCount() % 3 == 2){
98        // do not create nodes for every subtree
99        return;
100     }
101     for (int i = 0; i < children; i++){
102       Node child = graph.createNode();
103       graph.createEdge(root, child);
104       graph.getRealizer(child).setFillColor(layerColors[layer % layerColors.length]);
105       if (layers > 0){
106         createChildren(graph, child, children, layer+1, layers-1);
107       }
108     }
109   }
110 
111   protected boolean isDeletionEnabled(){
112     return false;
113   }
114 
115   protected boolean isClipboardEnabled() {
116     return false;
117   }
118 
119   protected void registerViewModes() {
120     EditMode editMode = new TreeCreateEditMode();
121     view.addViewMode( editMode );
122   }
123 
124   public void calcLayout(){
125     if (!view.getGraph2D().isEmpty()){
126       Cursor oldCursor = view.getViewCursor();
127       try {
128         view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
129         view.applyLayoutAnimated(treeLayouter);
130       } finally {
131         view.setViewCursor(oldCursor);
132       }
133     }
134   }
135 
136   final class TreeCreateChildEdgeMode extends CreateChildEdgeMode {
137     public void mouseReleasedLeft(double x, double y) {
138       // fire event to mark start of child creation for undo/redo
139       getGraph2D().firePreEvent();
140       super.mouseReleasedLeft(x, y);
141     }
142 
143     protected void edgeCreated(Edge e){
144       Graph2D g = getGraph2D();
145       g.setRealizer(e.target(), activeDummyTargetRealizer);
146       g.unselectAll();
147       calcLayout();
148 
149       // fire event to mark end of child creation for undo/redo
150       getGraph2D().firePostEvent();
151     }
152 
153     NodeRealizer activeDummyTargetRealizer;
154 
155     protected boolean acceptSourceNode(Node source, double x, double y) {
156       final boolean accept = super.acceptSourceNode(source, x, y);
157       activeDummyTargetRealizer = createChildNodeRealizer();
158       int depth = 1;
159       for (Node n = source; n.inDegree() > 0; n = n.firstInEdge().source()){
160               depth++;
161       }
162       activeDummyTargetRealizer.setFillColor(layerColors[depth % layerColors.length]);
163       return accept;
164     }
165 
166     protected NodeRealizer createDummyTargetNodeRealizer(double x, double y) {
167       return activeDummyTargetRealizer;
168     }
169 
170     protected NodeRealizer createChildNodeRealizer()
171     {
172       NodeRealizer retValue;
173       retValue = super.createChildNodeRealizer();
174       retValue.setLabelText("");
175       return retValue;
176     }
177 
178   }
179 
180   final class TreeHotSpotMode extends HotSpotMode {
181     public void mouseReleasedLeft(double x, double y)
182     {
183       super.mouseReleasedLeft(x, y);
184       calcLayout();
185     }
186   }
187 
188   final class TreeCreateEditMode extends EditMode {
189     TreeCreateEditMode(){
190       super();
191       setMoveSelectionMode(new TreeMoveSelectionMode());
192       setCreateEdgeMode(new TreeCreateChildEdgeMode());
193       setHotSpotMode(new TreeHotSpotMode());
194     }
195 
196     public boolean doAllowNodeCreation()
197     {
198       return getGraph2D().N() == 0;
199     }
200   }
201 
202   final class TreeMoveSelectionMode extends PortAssignmentMoveSelectionMode {
203     TreeMoveSelectionMode(){
204       super(null, null);
205     }
206 
207     protected void selectionMovedAction(double dx, double dy, double x, double y)
208     {
209       super.selectionMovedAction(dx, dy, x, y);
210       calcLayout();
211     }
212   }
213 
214   /**
215    * Launches this demo.
216    */
217   public static void main(String[] args) {
218     EventQueue.invokeLater(new Runnable() {
219       public void run() {
220         Locale.setDefault(Locale.ENGLISH);
221         initLnF();
222         (new DendrogramLayouterDemo()).start("Dendrogram Demo");
223       }
224     });
225   }
226 }
227