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.anim.AnimationPlayer;
32  import y.base.Edge;
33  import y.base.EdgeMap;
34  import y.base.Node;
35  import y.base.NodeCursor;
36  import y.base.NodeMap;
37  import y.geom.YPoint;
38  import y.layout.PortConstraint;
39  import y.layout.PortConstraintKeys;
40  import y.layout.tree.AbstractRotatableNodePlacer.RootAlignment;
41  import y.layout.tree.FromSketchNodePlacer;
42  import y.layout.tree.GenericTreeLayouter;
43  import y.layout.tree.NodePlacer;
44  import y.layout.tree.SimpleNodePlacer;
45  import y.util.DataProviderAdapter;
46  import y.view.CreateChildEdgeMode;
47  import y.view.EdgeRealizer;
48  import y.view.EditMode;
49  import y.view.Graph2D;
50  import y.view.HotSpotMode;
51  import y.view.NodeRealizer;
52  import y.view.PopupMode;
53  import y.view.PortAssignmentMoveSelectionMode;
54  
55  import javax.swing.AbstractAction;
56  import java.awt.Color;
57  import java.awt.Cursor;
58  import java.awt.event.ActionEvent;
59  import java.lang.reflect.Method;
60  
61  /**
62   * The AbstractTreeDemo is a base class for several tree demos.
63   * It contains ViewModes and other helper methods for tree manipulation and visualization.
64   **/
65  public abstract class AbstractTreeDemo extends DemoBase {
66    protected GenericTreeLayouter treeLayouter = new GenericTreeLayouter();
67  
68    protected EdgeMap sourcePortMap;
69    protected EdgeMap targetPortMap;
70    protected NodeMap portAssignmentMap;
71    protected NodeMap nodePlacerMap;
72    protected PortAssignmentMoveSelectionMode portAssignmentMoveMode = new TreePortAssignmentMode();
73    protected Color[] layerColors = {Color.red, Color.orange, Color.yellow, Color.blue, Color.cyan,
74        Color.green};
75  
76    /**
77     * Instantiates a new AbstractDemo.
78     */
79    protected AbstractTreeDemo() {
80      view.addViewMode(new TreeCreateEditMode());
81  
82      AnimationPlayer animationPlayer = new AnimationPlayer();
83      animationPlayer.addAnimationListener(view);
84  
85      Graph2D graph = view.getGraph2D();
86  
87      sourcePortMap = graph.createEdgeMap();
88      targetPortMap = graph.createEdgeMap();
89      portAssignmentMap = graph.createNodeMap();
90      nodePlacerMap = graph.createNodeMap();
91      graph.addDataProvider(GenericTreeLayouter.NODE_PLACER_DPKEY, new DataProviderAdapter() {
92        public Object get( final Object dataHolder ) {
93          if (((Node) dataHolder).outDegree() == 0) {
94            return null;
95          } else {
96            return nodePlacerMap.get(dataHolder);
97          }
98        }
99      });
100     graph.addDataProvider(GenericTreeLayouter.PORT_ASSIGNMENT_DPKEY, portAssignmentMap);
101     graph.addDataProvider(GenericTreeLayouter.CHILD_COMPARATOR_DPKEY, new ChildEdgeComparatorProvider());
102     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
103     graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
104 
105     portAssignmentMoveMode.setSpc(sourcePortMap);
106     portAssignmentMoveMode.setTpc(targetPortMap);
107   }
108 
109   /**
110    * Set the NodePlacer for the given node.
111    */
112   public void setNodePlacer(Node node, NodePlacer placer) {
113     nodePlacerMap.set(node, placer);
114   }
115 
116   /**
117    * Calculate the layout and update the view (using an animation).
118    */
119   public void calcLayout() {
120     if (!view.getGraph2D().isEmpty()) {
121       Cursor oldCursor = view.getViewCursor();
122       try {
123         view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
124         view.applyLayoutAnimated(treeLayouter);
125       } finally {
126         view.setViewCursor(oldCursor);
127       }
128     }
129   }
130 
131   /**
132    * May be overridden by subclasses.
133    */
134   protected PopupMode createTreePopupMode() {
135     return null;
136   }
137 
138   /**
139    * May be overridden by subclasses.
140    */
141   protected void registerViewModes() {
142   }
143 
144   protected NodePlacer createDefaultNodePlacer() {
145     return new SimpleNodePlacer();
146   }
147 
148   protected final class ChildEdgeComparatorProvider extends DataProviderAdapter {
149     public Object get(Object dataHolder) {
150       NodePlacer placer = (NodePlacer) nodePlacerMap.get(dataHolder);
151       if (placer instanceof FromSketchNodePlacer){
152         return ((FromSketchNodePlacer) placer).createFromSketchComparator();
153       }
154       return null;
155     }
156   }
157 
158   private final class TreeHotSpotMode extends HotSpotMode {
159     public void mouseReleasedLeft(double x, double y) {
160       super.mouseReleasedLeft(x, y);
161       calcLayout();
162     }
163   }
164 
165   protected class TreeCreateEditMode extends EditMode {
166     TreeCreateEditMode() {
167       if (portAssignmentMoveMode == null) {
168         throw new IllegalStateException("portAssignmentMoveMode is null");
169       }
170       setMoveSelectionMode(portAssignmentMoveMode);
171       setCreateEdgeMode(new TreeCreateChildEdgeMode());
172       setHotSpotMode(new TreeHotSpotMode());
173       setPopupMode(AbstractTreeDemo.this.createTreePopupMode());
174     }
175 
176     public boolean doAllowNodeCreation() {
177       return getGraph2D().N() == 0;
178     }
179 
180     protected void nodeCreated(Node v) {
181       super.nodeCreated(v);
182       setNodePlacer(v, createDefaultNodePlacer());
183     }
184   }
185 
186   private final class TreeCreateChildEdgeMode extends CreateChildEdgeMode {
187 
188     public void mouseReleasedLeft(double x, double y) {
189       // fire event to mark start of edge creation for undo/redo
190       getGraph2D().firePreEvent();
191       super.mouseReleasedLeft(x, y);
192     }
193 
194     protected void edgeCreated(Edge edge) {
195       int depth = 1;
196       for (Node node = edge.source(); node.inDegree() > 0; node = node.firstInEdge().source()) {
197         depth++;
198       }
199       Graph2D g = getGraph2D();
200       g.getRealizer(edge.target()).setFillColor(layerColors[depth % layerColors.length]);
201       EdgeRealizer er = g.getRealizer(edge);
202       if (nodePlacerMap.get(edge.source()) == null) {
203         parseNodePlacement(g, edge, er);
204       }
205 
206       setNodePlacer(edge.target(), createDefaultNodePlacer());
207       parseTargetPort(g, edge, er);
208       g.unselectAll();
209       calcLayout();
210 
211       // fire event to mark end of edge creation (including layout) for undo/redo
212       getGraph2D().firePostEvent();
213     }
214 
215     protected NodeRealizer activeDummyTargetRealizer;
216 
217     protected boolean acceptSourceNode(Node source, double x, double y) {
218       final boolean accept = super.acceptSourceNode(source, x, y);
219       activeDummyTargetRealizer = createChildNodeRealizer();
220       int depth = 1;
221       for (Node n = source; n.inDegree() > 0; n = n.firstInEdge().source()){
222               depth++;
223       }
224       activeDummyTargetRealizer.setFillColor(layerColors[depth % layerColors.length]);
225       return accept;
226     }
227 
228     protected NodeRealizer createDummyTargetNodeRealizer(double x, double y) {
229       return activeDummyTargetRealizer;
230     }
231 
232     private void parseNodePlacement(Graph2D g, Edge e, EdgeRealizer er) {
233       nodePlacerMap.set(e.source(), new SimpleNodePlacer());
234     }
235 
236     private void parseTargetPort(Graph2D g, Edge e, EdgeRealizer er) {
237       if (er.bendCount() > 0) {
238         YPoint lastPoint = new YPoint(er.lastBend().getX(), er.lastBend().getY());
239         NodeRealizer target = g.getRealizer(e.target());
240         double dx = lastPoint.x - target.getCenterX();
241         double dy = lastPoint.y - target.getCenterY();
242         byte side;
243         if (Math.abs(dx) > Math.abs(dy)) {
244           if (dx > 0.0) {
245             side = PortConstraint.EAST;
246           } else {
247             side = PortConstraint.WEST;
248           }
249         } else {
250           if (dy > 0.0) {
251             side = PortConstraint.SOUTH;
252           } else {
253             side = PortConstraint.NORTH;
254           }
255         }
256         targetPortMap.set(e, PortConstraint.create(side));
257       }
258     }
259 
260     protected NodeRealizer createChildNodeRealizer() {
261       NodeRealizer retValue = super.createChildNodeRealizer();
262       retValue.setLabelText("");
263       return retValue;
264     }
265 
266   }
267 
268   protected class SetHorizontalAlignmentAction extends AbstractAction {
269     private RootAlignment alignment;
270 
271     protected SetHorizontalAlignmentAction(String name, RootAlignment alignment) {
272       super(name);
273       this.alignment = alignment;
274     }
275 
276     public void actionPerformed(ActionEvent e) {
277       for (NodeCursor nodeCursor = view.getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next()) {
278         Node node = nodeCursor.node();
279         NodePlacer nodePlacer = (NodePlacer) nodePlacerMap.get(node);
280 
281         try {
282           Method method = nodePlacer.getClass().getMethod("setRootAlignment", new Class[]{RootAlignment.class});
283           method.invoke(nodePlacer, new Object[]{alignment});
284         } catch (Exception ex) {
285         }
286       }
287       calcLayout();
288     }
289   }
290 
291   abstract class SetNodePlacerAction extends AbstractAction {
292     protected SetNodePlacerAction(String name) {
293       super(name);
294     }
295 
296     public void actionPerformed(ActionEvent e) {
297       for (NodeCursor nodeCursor = view.getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next()) {
298         Node node = nodeCursor.node();
299         nodePlacerMap.set(node, createNodePlacer());
300       }
301       calcLayout();
302     }
303 
304     protected abstract NodePlacer createNodePlacer();
305   }
306 
307   private final class TreePortAssignmentMode extends PortAssignmentMoveSelectionMode {
308     TreePortAssignmentMode() {
309       super(null, null);
310     }
311 
312     protected boolean isPortReassignmentAllowed(Edge edge, boolean source) {
313       return !source;
314     }
315 
316     protected void selectionMovedAction(double dx, double dy, double x, double y) {
317       super.selectionMovedAction(dx, dy, x, y);
318       calcLayout();
319     }
320 
321   }
322 }
323