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.hierarchy.HierarchyDemo;
17  
18  import y.base.DataMap;
19  import y.base.EdgeCursor;
20  import y.base.Node;
21  import y.base.NodeCursor;
22  import y.base.NodeList;
23  import y.layout.BufferedLayouter;
24  import y.layout.GraphLayout;
25  import y.layout.hierarchic.IncrementalHierarchicLayouter;
26  import y.layout.hierarchic.incremental.IncrementalHintsFactory;
27  import y.option.ConstraintManager;
28  import y.option.OptionHandler;
29  import y.option.OptionItem;
30  import y.util.Maps;
31  import y.view.Graph2D;
32  import y.view.LayoutMorpher;
33  import y.view.hierarchy.DefaultHierarchyGraphFactory;
34  import y.view.hierarchy.GroupLayoutConfigurator;
35  import y.view.hierarchy.ProxyAutoBoundsNodeRealizer;
36  
37  import javax.swing.AbstractAction;
38  import javax.swing.JToolBar;
39  import java.awt.Dimension;
40  import java.awt.Frame;
41  import java.awt.event.ActionEvent;
42  
43  
44  /**
45   * This demo showcases how IncrementalHierarchicLayouter can be used to fully or incrementally
46   * layout hierarchically nested graphs. The demo supports automatic relayout after expanding folder nodes,
47   * collapsing group nodes. Furthermore it provides toolbar buttons that
48   * trigger full layout and incremental relayout. A settings dialog for group layout options is provided as well.
49   * In incremental layout mode all selected elements are added incrementally to the existing layout.
50   */
51  public class IncrementalHierarchicGroupDemo extends HierarchyDemo {
52  
53    IncrementalHierarchicLayouter layouter;
54    OptionHandler groupLayoutOptions;
55  
56    public IncrementalHierarchicGroupDemo() {
57  
58         //configure layout algorithm
59      layouter = new IncrementalHierarchicLayouter();
60      layouter.getEdgeLayoutDescriptor().setOrthogonallyRouted(true);
61      layouter.setRecursiveGroupLayeringEnabled(false);
62  
63      //prepare option handler for group layout options
64      Object[] groupStrategyEnum = {"Global Layering", "Recursive Layering"};
65      Object[] groupAlignmentEnum = {"Top", "Center", "Bottom"};
66      groupLayoutOptions = new OptionHandler("Grouplayout Options");
67      ConstraintManager cm = new ConstraintManager(groupLayoutOptions);
68      OptionItem gsi = groupLayoutOptions.addEnum("Group Layering Strategy", groupStrategyEnum, 0);
69      OptionItem eci = groupLayoutOptions.addBool("Enable Compact Layering", true);
70      OptionItem gai = groupLayoutOptions.addEnum("Group Alignment", groupAlignmentEnum, 0);
71      cm.setEnabledOnValueEquals(gsi, "Recursive Layering", eci);
72      cm.setEnabledOnValueEquals(gsi, "Recursive Layering", gai);
73      cm.setEnabledOnCondition(cm.createConditionValueEquals(gsi, "Recursive Layering").and(cm.createConditionValueEquals(eci, Boolean.TRUE).inverse()), gai);
74  
75      //configure default graphics for default node realizers. defaults are adopted
76      //from nodes contained in initial graph.
77      Graph2D graph = view.getGraph2D();
78      for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
79        Node n = nc.node();
80        if (hierarchy.isNormalNode(n)) {
81          graph.setDefaultNodeRealizer(graph.getRealizer(n).createCopy());
82          break;
83        }
84      }
85      for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
86        Node n = nc.node();
87        if (!hierarchy.isNormalNode(n)) {
88          DefaultHierarchyGraphFactory hgf = (DefaultHierarchyGraphFactory) hierarchy.getGraphFactory();
89          if(graph.getRealizer(n) instanceof ProxyAutoBoundsNodeRealizer) {
90            hgf.setProxyNodeRealizerEnabled(true);
91            ProxyAutoBoundsNodeRealizer pnr = (ProxyAutoBoundsNodeRealizer) graph.getRealizer(n);
92            hgf.setDefaultGroupNodeRealizer(pnr.getRealizer(0).createCopy());
93            hgf.setDefaultFolderNodeRealizer(pnr.getRealizer(1).createCopy());
94            break;
95          }
96        }
97      }
98  
99      view.fitContent();
100   }
101 
102   /**
103    * Loads the initial graph
104    */
105   protected void loadInitialGraph() {
106     loadGraph("resource/grouping.ygf");
107   }
108 
109   /**
110    * Creates the toolbar for the demo.
111    */
112   protected JToolBar createToolBar() {
113     JToolBar toolBar = super.createToolBar();
114     toolBar.add(new AbstractAction("Incremental Layout") {
115       public void actionPerformed(ActionEvent e) {
116         layoutIncrementally();
117       }
118     });
119     toolBar.add(new AbstractAction("New Layout") {
120       public void actionPerformed(ActionEvent e) {
121         layout();
122       }
123     });
124     toolBar.add(new AbstractAction("Group Layout Options...") {
125       public void actionPerformed(ActionEvent e) {
126         groupLayoutOptions.showEditor((Frame)view.getTopLevelAncestor());
127         configureGroupLayout();
128       }
129     });
130     return toolBar;
131   }
132 
133   /**
134    * Configures the layouter options relevant for grouping.
135    */
136   void configureGroupLayout() {
137     Object gsi = groupLayoutOptions.get("Group Layering Strategy");
138     if ("Recursive Layering".equals(gsi)) {
139       layouter.setRecursiveGroupLayeringEnabled(true);
140     } else if ("Global Layering".equals(gsi)) {
141       layouter.setRecursiveGroupLayeringEnabled(false);
142     }
143 
144     layouter.setGroupCompactionEnabled(groupLayoutOptions.getBool("Enable Compact Layering"));
145 
146     Object gai = groupLayoutOptions.get("Group Alignment");
147     if ("Top".equals(gai)) {
148       layouter.setGroupAlignmentPolicy(IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_TOP);
149     } else if ("Center".equals(gai)) {
150       layouter.setGroupAlignmentPolicy(IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_CENTER);
151     }
152     if ("Bottom".equals(gai)) {
153       layouter.setGroupAlignmentPolicy(IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_BOTTOM);
154     }
155   }
156 
157   /**
158    * Performs incremental layout. All selected elements will be treated incrementally.
159    */
160   void layoutIncrementally() {
161     Graph2D graph = view.getGraph2D();
162 
163     layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
164 
165     // create storage for both nodes and edges
166     DataMap incrementalElements = Maps.createHashedDataMap();
167     // configure the mode
168     final IncrementalHintsFactory ihf = layouter.createIncrementalHintsFactory();
169 
170     for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
171       incrementalElements.set(nc.node(), ihf.createLayerIncrementallyHint(nc.node()));
172     }
173 
174     for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
175       incrementalElements.set(ec.edge(), ihf.createSequenceIncrementallyHint(ec.edge()));
176     }
177     graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, incrementalElements);
178 
179 
180     GroupLayoutConfigurator glc = new GroupLayoutConfigurator(graph);
181 
182     glc.prepareAll();
183 
184  //   final DataProvider origParentDP = graph.getDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY);
185  //   if(origParentDP != null) {
186  //     DataProvider fakeParentDP = new DataProviderAdapter() {
187  //       public Object get(Object dataHolder) {
188  //         Node currentNode = (Node) dataHolder;
189  //         Node origParent = (Node) origParentDP.get(dataHolder);
190  //         if(origParent == null) {
191  //           return null;
192  //         }
193  //         if(origParent.getGraph() == currentNode.getGraph()) {
194  //           //we live in the same graph, everything is ok...
195  //           return origParent;
196  //         }
197  //         else {
198  //           //return virtual root item instead
199  //           return null;
200  //         }
201  //       }
202  //     };
203  //     graph.addDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY, fakeParentDP);
204  //   }
205     GraphLayout gl = new BufferedLayouter(layouter).calcLayout(graph);
206     new LayoutMorpher(view, gl).execute();
207  //   if(origParentDP != null) {
208  //     graph.addDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY, origParentDP);
209  //   }
210     glc.restoreAll();
211     
212     graph.removeDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY);
213   }
214 
215   /**
216    * Performs global layout. The new layout can strongly differ from the existing layout.
217    */
218   void layout() {
219     final Graph2D graph = view.getGraph2D();
220     layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
221     GroupLayoutConfigurator glc = new GroupLayoutConfigurator(graph);
222     glc.prepareAll();
223  //   final DataProvider origParentDP = graph.getDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY);
224 //
225 //    if(origParentDP != null) {
226 //      DataProvider fakeParentDP = new DataProviderAdapter() {
227 //        public Object get(Object dataHolder) {
228 //          Node currentNode = (Node) dataHolder;
229 //          Node origParent = (Node) origParentDP.get(dataHolder);
230 //          if(origParent == null) {
231 //            return null;
232 //          }
233 //          if(origParent.getGraph() == currentNode.getGraph()) {
234 //            //we live in the same graph, everything is ok...
235 //            return origParent;
236 //          }
237 //          else {
238 //            //return virtual root item instead
239 //            return null;
240 //          }
241 //        }
242 //      };
243 //      graph.addDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY, fakeParentDP);
244 //    }
245     GraphLayout gl = new BufferedLayouter(layouter).calcLayout(graph);
246     new LayoutMorpher(view, gl).execute();
247     //if(origParentDP != null) {
248 //      graph.addDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY, origParentDP);
249     //}
250     glc.restoreAll();
251   }
252 
253 
254   /**
255    * Expand a folder node. After expanding the folder node, an incremental layout is automatically triggered.
256    * For this, the expanded node and all of its descendants will be treated as incremental elements.
257    */
258   protected void openFolder(Node folderNode) {
259     NodeList children = new NodeList(hierarchy.getInnerGraph(folderNode).nodes());
260     super.openFolder(folderNode);
261 
262     Graph2D graph = view.getGraph2D();
263 
264     graph.unselectAll();
265     graph.setSelected(folderNode, true);
266     for(NodeCursor nc = children.nodes(); nc.ok(); nc.next()) {
267       graph.setSelected(nc.node(), true);
268     }
269 
270     layoutIncrementally();
271 
272     graph.unselectAll();
273     graph.setSelected(folderNode, true);
274 
275     graph.updateViews();
276   }
277 
278   public void navigateToInnerGraph(Node folderNode)
279   {
280     if(hierarchy.isFolderNode(folderNode))
281     {
282       Graph2D innerGraph =  (Graph2D)hierarchy.getInnerGraph(folderNode);
283       view.setGraph2D(innerGraph);
284       layout();
285       innerGraph.updateViews();
286     }
287   }
288 
289   /**
290    * Collapse a group node. After collapsing the group node, an incremental layout is automatically triggered.
291    * For this, the collapsed node is treated as an incremental element.
292    */
293   protected void closeGroup(Node groupNode) {
294     super.closeGroup(groupNode);
295 
296     Graph2D graph = view.getGraph2D();
297     graph.unselectAll();
298     graph.setSelected(groupNode, true);
299     for (EdgeCursor ec = groupNode.edges(); ec.ok(); ec.next()) {
300       graph.setSelected(ec.edge(), true);
301     }
302 
303     layoutIncrementally();
304     graph.unselectAll();
305 
306     graph.updateViews();
307   }
308 
309   /**
310    * Launches this demo.
311    */
312   public static void main(String[] args) {
313     initLnF();
314     (new IncrementalHierarchicGroupDemo()).start();
315   }
316 }
317