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.mixed;
15  
16  import demo.view.hierarchy.GroupingDemo;
17  import y.base.DataProvider;
18  import y.base.Edge;
19  import y.base.EdgeCursor;
20  import y.base.EdgeList;
21  import y.base.EdgeMap;
22  import y.layout.LayoutGraph;
23  import y.layout.LayoutTool;
24  import y.layout.Layouter;
25  import y.layout.ParallelEdgeLayouter;
26  import y.layout.circular.CircularLayouter;
27  import y.layout.grouping.RecursiveGroupLayouter;
28  import y.layout.hierarchic.IncrementalHierarchicLayouter;
29  import y.layout.organic.SmartOrganicLayouter;
30  import y.layout.orthogonal.OrthogonalGroupLayouter;
31  import y.layout.router.GroupNodeRouterStage;
32  import y.layout.router.OrthogonalEdgeRouter;
33  import y.util.DataProviderAdapter;
34  import y.view.Graph2D;
35  import y.view.Graph2DLayoutExecutor;
36  import y.view.Graph2DView;
37  import y.view.Graph2DViewActions;
38  
39  import javax.swing.AbstractAction;
40  import javax.swing.Action;
41  import javax.swing.ActionMap;
42  import javax.swing.JComboBox;
43  import javax.swing.JMenu;
44  import javax.swing.JToggleButton;
45  import javax.swing.JToolBar;
46  import java.awt.EventQueue;
47  import java.awt.event.ActionEvent;
48  import java.awt.event.ActionListener;
49  import java.util.Locale;
50  
51  /**
52   * Shows the Recursive Group Layouter. The content of each group node is recursively laid out with the specified
53   * layouter, i.e., the layouter is applied to each group node separately. Note that the
54   * {@link y.layout.grouping.RecursiveGroupLayouter}
55   * also supports to specify different layout algorithms for different group nodes, see {@link MixedLayoutDemo}.
56   */
57  public class RecursiveLayoutDemo extends GroupingDemo {
58    private static final int TYPE_ORTHOGONAL = 0;
59    private static final int TYPE_HIERARCHIC = 1;
60    private static final int TYPE_CIRCULAR = 2;
61    private static final int TYPE_ORGANIC = 3;
62  
63    private int layoutType;
64    private boolean useInterEdgeRouter;
65  
66    public RecursiveLayoutDemo() {
67      this(null);
68    }
69  
70    public RecursiveLayoutDemo(final String helpFilePath) {
71      super();
72      addHelpPane(helpFilePath);
73    }
74  
75    protected void loadInitialGraph() {
76      loadGraph("resource/recursive.graphml");
77    }
78  
79    /**
80     * Adds an extra layout action to the toolbar
81     */
82    protected JToolBar createToolBar() {
83      layoutType = TYPE_ORTHOGONAL;
84      final JComboBox layoutTypeSelection = new JComboBox(
85          new String[]{"Orthogonal Style", "Hierarchic Style", "Circular Style", "Organic Style"});
86      layoutTypeSelection.setSelectedIndex(layoutType);
87      layoutTypeSelection.setMaximumSize(layoutTypeSelection.getPreferredSize());
88      layoutTypeSelection.addActionListener(new ActionListener() {
89        public void actionPerformed(ActionEvent e) {
90          switch (layoutTypeSelection.getSelectedIndex()) {
91            default:
92            case 0:
93              layoutType = TYPE_ORTHOGONAL;
94              break;
95            case 1:
96              layoutType = TYPE_HIERARCHIC;
97              break;
98            case 2:
99              layoutType = TYPE_CIRCULAR;
100             break;
101           case 3:
102             layoutType = TYPE_ORGANIC;
103             break;
104         }
105         doLayout();
106       }
107     });
108 
109     useInterEdgeRouter = true;
110     final JToggleButton toggleRouteInterEdges = new JToggleButton("Inter-Edge Routing", useInterEdgeRouter);
111     toggleRouteInterEdges.addActionListener(new ActionListener() {
112       public void actionPerformed(ActionEvent e) {
113         useInterEdgeRouter = toggleRouteInterEdges.isSelected();
114         doLayout();
115       }
116     });
117 
118     final Action layoutAction = new AbstractAction(
119             "Layout", SHARED_LAYOUT_ICON) {
120       public void actionPerformed(ActionEvent e) {
121         doLayout();
122       }
123     };
124 
125     JToolBar toolBar = super.createToolBar();
126     toolBar.addSeparator();
127     toolBar.add(createActionControl(layoutAction));
128     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
129     toolBar.add(layoutTypeSelection);
130     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
131     toolBar.add(toggleRouteInterEdges);
132     return toolBar;
133   }
134 
135   /**
136    * Register key bindings for our custom actions.
137    */
138   protected void registerViewActions() {
139     super.registerViewActions();
140 
141     ActionMap actionMap = view.getCanvasComponent().getActionMap();
142     actionMap.put(Graph2DViewActions.CLOSE_GROUPS, new MyCloseGroupsAction(view));
143     actionMap.put(Graph2DViewActions.OPEN_FOLDERS, new MyOpenFoldersAction(view));
144   }
145 
146   /**
147    * Populates the "Grouping" menu with grouping specific actions.
148    */
149   protected void populateGroupingMenu(JMenu hierarchyMenu) {
150     // Predefined actions for open/close groups
151     registerAction(hierarchyMenu, Graph2DViewActions.CLOSE_GROUPS, true);
152     registerAction(hierarchyMenu, Graph2DViewActions.OPEN_FOLDERS, true);
153 
154     hierarchyMenu.addSeparator();
155 
156     // Predefined actions for group/fold/ungroup
157     registerAction(hierarchyMenu, Graph2DViewActions.GROUP_SELECTION, true);
158     registerAction(hierarchyMenu, Graph2DViewActions.UNGROUP_SELECTION, true);
159     registerAction(hierarchyMenu, Graph2DViewActions.FOLD_SELECTION, true);
160   }
161 
162   /**
163    * Performs the common behavior and applies a layout afterwards.
164    */
165   class MyCloseGroupsAction extends Graph2DViewActions.CloseGroupsAction {
166 
167     MyCloseGroupsAction(Graph2DView view) {
168       super(view);
169     }
170 
171     public void actionPerformed(ActionEvent e) {
172       super.actionPerformed(e);
173       doLayout();
174     }
175   }
176 
177   /**
178    * Performs the common behavior and applies a layout afterwards.
179    */
180   class MyOpenFoldersAction extends Graph2DViewActions.OpenFoldersAction {
181 
182     MyOpenFoldersAction(Graph2DView view) {
183       super(view);
184     }
185 
186     public void actionPerformed(ActionEvent e) {
187       super.actionPerformed(e);
188       doLayout();
189     }
190   }
191 
192   void doLayout() {
193     final RecursiveGroupLayouter rgl;
194     final Layouter coreLayout;
195 
196     if (layoutType == TYPE_ORTHOGONAL) {
197       coreLayout = new OrthogonalGroupLayouter();
198       rgl = createOrthogonalRecursiveGroupLayout(coreLayout, OrthogonalEdgeRouter.MONOTONIC_NONE);
199 
200     } else if (layoutType == TYPE_HIERARCHIC) {
201       final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
202       ihl.setOrthogonallyRouted(true);
203       coreLayout = ihl;
204       rgl = createOrthogonalRecursiveGroupLayout(coreLayout, OrthogonalEdgeRouter.MONOTONIC_VERTICAL);
205 
206     } else if (layoutType == TYPE_ORGANIC) {
207       coreLayout = new SmartOrganicLayouter();
208       rgl = createRecursiveGroupLayout(coreLayout);
209       rgl.setAutoAssignPortCandidatesEnabled(true);
210 
211     } else {
212       final CircularLayouter cl = new CircularLayouter();
213       cl.setParallelEdgeLayouter(createParallelEdgeLayouter());
214 
215       coreLayout = cl;
216       rgl = createRecursiveGroupLayout(coreLayout);
217     }
218 
219     final Graph2D graph = view.getGraph2D();
220     try {
221       // map each group node to its corresponding layout algorithm
222       graph.addDataProvider(RecursiveGroupLayouter.GROUP_NODE_LAYOUTER_DPKEY, new DataProviderAdapter() {
223         public Object get(Object dataHolder) {
224           return coreLayout;
225         }
226       });
227 
228       new Graph2DLayoutExecutor().doLayout(view, rgl);
229       view.fitContent();
230 
231     } finally {
232       graph.removeDataProvider(RecursiveGroupLayouter.GROUP_NODE_LAYOUTER_DPKEY);
233     }
234   }
235 
236   RecursiveGroupLayouter createOrthogonalRecursiveGroupLayout(Layouter coreLayout, final byte monotonicPathType) {
237     final RecursiveGroupLayouter rgl = new RecursiveGroupLayouter(coreLayout) {
238       protected void routeInterEdges(LayoutGraph graph, EdgeList interEdges) {
239         if (useInterEdgeRouter) {
240           DataProvider selectedEdges = graph.getDataProvider(Layouter.SELECTED_EDGES); //backup selected edges
241 
242           EdgeMap edge2IsInterEdge = graph.createEdgeMap();
243           for (EdgeCursor ec = interEdges.edges(); ec.ok(); ec.next()) {
244             edge2IsInterEdge.setBool(ec.edge(), true);
245           }
246           graph.addDataProvider(Layouter.SELECTED_EDGES, edge2IsInterEdge);
247 
248           //route inter-edges
249           OrthogonalEdgeRouter oer = createOrthogonalEdgeRouter();
250           if (monotonicPathType != OrthogonalEdgeRouter.MONOTONIC_NONE) {
251             oer.setMonotonicPathRestriction(monotonicPathType);
252           }
253           new GroupNodeRouterStage(oer).doLayout(graph);
254 
255           //restore originally selected edges
256           if (selectedEdges != null) {
257             graph.addDataProvider(Layouter.SELECTED_EDGES, selectedEdges);
258           } else {
259             graph.removeDataProvider(Layouter.SELECTED_EDGES);
260           }
261           graph.disposeEdgeMap(edge2IsInterEdge);
262         } else {
263           super.routeInterEdges(graph, interEdges);
264         }
265       }
266     };
267 
268     rgl.setConsiderSketchEnabled(true);
269 
270     return rgl;
271   }
272 
273   RecursiveGroupLayouter createRecursiveGroupLayout(Layouter coreLayout) {
274     RecursiveGroupLayouter rgl = new RecursiveGroupLayouter(coreLayout) {
275       protected void routeInterEdges(LayoutGraph graph, EdgeList interEdges) {
276         if (useInterEdgeRouter) {
277           //reset paths of inter-edges
278           EdgeMap edge2IsInterEdge = graph.createEdgeMap();
279           for (EdgeCursor ec = interEdges.edges(); ec.ok(); ec.next()) {
280             Edge e = ec.edge();
281             edge2IsInterEdge.setBool(e, true);
282             LayoutTool.resetPath(graph, e);
283           }
284 
285           //layout parallel edges
286           graph.addDataProvider(ParallelEdgeLayouter.SCOPE_DPKEY, edge2IsInterEdge);
287           createParallelEdgeLayouter().doLayout(graph);
288           graph.removeDataProvider(ParallelEdgeLayouter.SCOPE_DPKEY);
289           graph.disposeEdgeMap(edge2IsInterEdge);
290         } else {
291           super.routeInterEdges(graph, interEdges);
292         }
293       }
294     };
295 
296     rgl.setConsiderSketchEnabled(true);
297 
298     return rgl;
299   }
300 
301   static OrthogonalEdgeRouter createOrthogonalEdgeRouter() {
302     OrthogonalEdgeRouter oer = new OrthogonalEdgeRouter();
303     oer.setCrossingCost(2.0);
304     oer.setLocalCrossingMinimizationEnabled(true);
305     oer.setReroutingEnabled(true);
306     oer.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
307     return oer;
308   }
309 
310   static ParallelEdgeLayouter createParallelEdgeLayouter() {
311     ParallelEdgeLayouter pel = new ParallelEdgeLayouter();
312     pel.setLineDistance(10.0);
313     pel.setJoinEndsEnabled(true);
314     return pel;
315   }
316 
317   /**
318    * Launches this demo.
319    */
320   public static void main(String[] args) {
321     EventQueue.invokeLater(new Runnable() {
322       public void run() {
323         Locale.setDefault(Locale.ENGLISH);
324         initLnF();
325         new RecursiveLayoutDemo("resource/recursivelayouthelp.html").start();
326       }
327     });
328   }
329 }
330