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