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