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.hierarchic;
15  
16  import demo.view.DemoBase;
17  import y.base.DataMap;
18  import y.base.Node;
19  import y.base.NodeCursor;
20  import y.base.NodeList;
21  import y.base.NodeMap;
22  import y.layout.BufferedLayouter;
23  import y.layout.GraphLayout;
24  import y.layout.Layouter;
25  import y.layout.hierarchic.IncrementalHierarchicLayouter;
26  import y.layout.hierarchic.incremental.IncrementalHintsFactory;
27  import y.layout.hierarchic.incremental.NodeLayoutDescriptor;
28  import y.layout.hierarchic.incremental.SimplexNodePlacer;
29  import y.layout.hierarchic.incremental.SwimLaneDescriptor;
30  import y.util.Maps;
31  import y.view.Arrow;
32  import y.view.EdgeRealizer;
33  import y.view.EditMode;
34  import y.view.Graph2D;
35  import y.view.LayoutMorpher;
36  import y.view.PopupMode;
37  
38  import javax.swing.AbstractAction;
39  import javax.swing.JMenu;
40  import javax.swing.JPopupMenu;
41  import javax.swing.JToolBar;
42  import java.awt.Cursor;
43  import java.awt.event.ActionEvent;
44  
45  /**
46   * <p>
47   * This simple demo shows how to use the swim lane feature of the 
48   * new {@link y.layout.hierarchic.IncrementalHierarchicLayouter}.
49   * </p>
50   * <p>
51   * It can either calculate a new layout or calculate a new layout given the current 
52   * sketch or incrementally layout selected nodes to an already existing graph whose
53   * layout is read from the current sketch. 
54   * </p>
55   * <p>
56   * Things to try:<br/>
57   * Create a graph and assign nodes to layers by editing the label of the nodes.
58   * Nodes with the same layer will be placed into the same swim lane. Swim lanes
59   * are sorted from left to right in ascending label order.
60   * <br/>
61   * Use the <b>Layout</b> button to lay it out from scratch.
62   * Modify the graph (move nodes and or bends), deselect all elements and 
63   * choose <b>Layout from Sketch</b> to recalculate the layout using the given sketch
64   * Add some nodes and connect them to the graph, select the newly added nodes
65   * and choose <b>Layout Incrementally</b> to incrementally "add" the selected 
66   * elements optimally into the existing graph.
67   * </p>
68   */
69  public class SimpleSwimLaneLayouterDemo extends DemoBase
70  {
71    /** Used to store the hints for the incremental layout */
72    private DataMap hintMap;
73  
74    /** Used to store the swim lane information for each node */
75    private NodeMap swimLaneMap;
76  
77    /** the layouter and the hintsfactory */
78    private IncrementalHierarchicLayouter hierarchicLayouter;
79    private IncrementalHintsFactory hintsFactory;
80  
81    /** the drawable for the swimlanes */
82    private SwimLaneDrawable swimLaneDrawable;
83  
84    public SimpleSwimLaneLayouterDemo()
85    {
86      final Graph2D graph = view.getGraph2D();
87      EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
88      defaultER.setArrow(Arrow.STANDARD);
89  
90      // create a map to store the hints for the incremental layout mechanism
91      hintMap = Maps.createHashedDataMap();
92      swimLaneMap = graph.createNodeMap();
93      graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, hintMap);
94      graph.addDataProvider(IncrementalHierarchicLayouter.SWIMLANE_DESCRIPTOR_DPKEY, swimLaneMap);
95  
96      // create the layouter
97      hierarchicLayouter = new IncrementalHierarchicLayouter();
98  
99      // set some defaults
100     hierarchicLayouter.getEdgeLayoutDescriptor().setMinimumFirstSegmentLength(15);
101     hierarchicLayouter.getEdgeLayoutDescriptor().setMinimumLastSegmentLength(20);
102     hierarchicLayouter.getEdgeLayoutDescriptor().setOrthogonallyRouted(true);
103     hierarchicLayouter.getEdgeLayoutDescriptor().setMinimumDistance(10.0d);
104 
105     hierarchicLayouter.getNodeLayoutDescriptor().setLayerAlignment(0.5d);
106     hierarchicLayouter.setMinimumLayerDistance(30.0d);
107     hierarchicLayouter.getNodeLayoutDescriptor().setNodeLabelMode(NodeLayoutDescriptor.NODE_LABEL_MODE_CONSIDER_FOR_DRAWING);
108     hierarchicLayouter.setConsiderNodeLabelsEnabled(true);
109 
110     // set the node placer to barycenter mode so that the results are centered
111     // nicely in the swimlanes if there is more room 
112     ((SimplexNodePlacer) hierarchicLayouter.getNodePlacer()).setBaryCenterModeEnabled(true);
113 
114     // get a reference to a hints factory
115     hintsFactory = hierarchicLayouter.createIncrementalHintsFactory();
116 
117     // disable the component layouter (optional)
118     hierarchicLayouter.setComponentLayouterEnabled(false);
119 
120     // add a drawable to visualize the swim lane geometry
121     view.addBackgroundDrawable(swimLaneDrawable = new SwimLaneDrawable(view.getGraph2D(), swimLaneMap));
122 
123     loadGraph( "resource/swimlane.gml" );
124     calcLayout( hierarchicLayouter );
125   }
126 
127   class LayoutFromSketchAction extends AbstractAction
128   {
129     LayoutFromSketchAction()
130     {
131       super("Layout From Sketch");
132     }
133 
134     public void actionPerformed(ActionEvent ev)
135     {
136       calcIncrementalLayout(new NodeList().nodes());
137     }
138   }
139 
140   class LayoutIncrementallyAction extends AbstractAction
141   {
142     LayoutIncrementallyAction()
143     {
144       super("Layout Incrementally");
145     }
146 
147     public void actionPerformed(ActionEvent ev)
148     {
149       calcIncrementalLayout(view.getGraph2D().selectedNodes());
150     }
151   }
152 
153   class LayoutAction extends AbstractAction
154   {
155     LayoutAction()
156     {
157       super("Layout");
158     }
159 
160     public void actionPerformed(ActionEvent ev)
161     {
162       calcFreshLayout();
163     }
164   }
165 
166   protected JToolBar createToolBar()
167   {
168     JToolBar tb = super.createToolBar();
169     tb.add(new LayoutAction());
170     tb.add(new LayoutFromSketchAction());
171     tb.add(new LayoutIncrementallyAction());
172     return tb;
173   }
174 
175   public void calcFreshLayout(){
176     hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
177     calcLayout(hierarchicLayouter);
178   }
179 
180   public void updateSwimLanes(){
181     for (NodeCursor nc = view.getGraph2D().nodes(); nc.ok(); nc.next()){
182       SwimLaneDescriptor sld = new SwimLaneDescriptor(view.getGraph2D().getLabelText(nc.node()));
183       sld.setLeftLaneInset(5);
184       sld.setRightLaneInset(5);
185       sld.setMinimumLaneWidth(100);
186       swimLaneMap.set(nc.node(), sld);
187     }
188   }
189 
190   public void calcIncrementalLayout(NodeCursor incrementalNodes){
191     try {
192       // mark nodes as "new"
193       for (incrementalNodes.toFirst(); incrementalNodes.ok(); incrementalNodes.next()){
194         hintMap.set(incrementalNodes.node(), hintsFactory.createLayerIncrementallyHint(incrementalNodes.node()));
195       }
196       // read the old nodes from the sketch
197       hierarchicLayouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
198       // calculate the layout incrementally
199       calcLayout(hierarchicLayouter);
200     } finally {
201       // reset the marks
202       for (incrementalNodes.toFirst(); incrementalNodes.ok(); incrementalNodes.next()){
203         hintMap.set(incrementalNodes.node(), null);
204       }
205     }
206   }
207 
208   protected void calcLayout(Layouter layouter){
209     Graph2D graph = view.getGraph2D();
210     if (!graph.isEmpty()){
211       Cursor oldCursor = view.getCanvasComponent().getCursor();
212       try {
213         // associate swim lane descriptors with each node...
214         updateSwimLanes();
215         GraphLayout result = new BufferedLayouter(layouter).calcLayout(view.getGraph2D());
216         LayoutMorpher morpher = new LayoutMorpher(view, result);
217         morpher.setSmoothViewTransform(true);
218         morpher.setPreferredDuration(300);
219         morpher.execute();
220       } finally {
221         view.getCanvasComponent().setCursor(oldCursor);
222       }
223     }
224     swimLaneDrawable.updateLanes();
225     view.fitContent();
226     view.updateView();
227   }
228 
229   protected EditMode createEditMode() {
230     EditMode editMode = super.createEditMode();
231     editMode.setPopupMode( new PopupMode( ){
232       public JPopupMenu getNodePopup( Node v ) {
233         return createPopup();
234       }
235 
236       private JPopupMenu createPopup() {
237         JPopupMenu menu = new JPopupMenu();
238         JMenu laneMenu = new JMenu( "Move to lane" );
239         menu.add( laneMenu );
240         laneMenu.add( new MoveSelectedNodesToLayerAction(1) );
241         laneMenu.add( new MoveSelectedNodesToLayerAction(2) );
242         laneMenu.add( new MoveSelectedNodesToLayerAction(3) );
243         laneMenu.add( new MoveSelectedNodesToLayerAction(4) );
244         laneMenu.add( new MoveSelectedNodesToLayerAction(5) );
245         laneMenu.add( new MoveSelectedNodesToLayerAction(6) );
246         return menu;
247       }
248 
249       public JPopupMenu getSelectionPopup( double x, double y ) {
250         return createPopup();
251       }
252     });
253     return editMode;
254   }
255 
256 
257   /**
258    * Launches this demo.
259    */
260   public static void main(String args[])
261   {
262     initLnF();
263     SimpleSwimLaneLayouterDemo demo = new SimpleSwimLaneLayouterDemo();
264     demo.start("Simple Swimlane Layout Demo");
265   }
266 
267   private class MoveSelectedNodesToLayerAction extends AbstractAction {
268     private int layer;
269 
270     MoveSelectedNodesToLayerAction( int layer ) {
271       super( String.valueOf( layer ) );
272       this.layer = layer;
273     }
274 
275     public void actionPerformed( ActionEvent e ) {
276       for ( NodeCursor nodeCursor = view.getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next() ) {
277         Node node = nodeCursor.node();
278         view.getGraph2D().setLabelText( node, String.valueOf( layer ) );
279       }
280       calcIncrementalLayout( view.getGraph2D().selectedNodes() );
281     }
282   }
283 }