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.hierarchic;
15  
16  import demo.view.hierarchy.GroupingDemo;
17  import y.base.DataMap;
18  import y.base.EdgeCursor;
19  import y.base.Node;
20  import y.base.NodeCursor;
21  import y.base.NodeList;
22  import y.layout.hierarchic.IncrementalHierarchicLayouter;
23  import y.layout.hierarchic.incremental.IncrementalHintsFactory;
24  import y.option.ConstraintManager;
25  import y.option.OptionHandler;
26  import y.option.OptionItem;
27  import y.util.Maps;
28  import y.view.Graph2D;
29  import y.view.Graph2DLayoutExecutor;
30  import y.view.Graph2DViewActions;
31  
32  import javax.swing.AbstractAction;
33  import javax.swing.Action;
34  import javax.swing.JLabel;
35  import javax.swing.JToolBar;
36  import java.awt.EventQueue;
37  import java.awt.event.ActionEvent;
38  import java.awt.event.ActionListener;
39  import java.util.Locale;
40  
41  
42  /**
43   * This demo showcases how IncrementalHierarchicLayouter can be used to fully or incrementally
44   * layout hierarchically nested graphs. The demo supports automatic relayout after expanding folder nodes,
45   * collapsing group nodes. Furthermore it provides toolbar buttons that
46   * trigger full layout and incremental relayout. A settings dialog for group layout options is provided as well.
47   * In incremental layout mode all selected elements are added incrementally to the existing layout.
48   *
49   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/incremental_hierarchical_layouter.html#ihl_hierarchically_organized_graphs">Section Layout of Grouped Graphs</a> in the yFiles for Java Developer's Guide
50   */
51  public class IncrementalHierarchicGroupDemo extends GroupingDemo {
52  
53    IncrementalHierarchicLayouter layouter;
54    OptionHandler groupLayoutOptions;
55  
56    public IncrementalHierarchicGroupDemo() {
57  
58      //configure layout algorithm
59      layouter = new IncrementalHierarchicLayouter();
60      layouter.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("Layout Properties");
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(
74          cm.createConditionValueEquals(eci, Boolean.TRUE).inverse()), gai);
75  
76      view.fitContent();
77    }
78  
79    /**
80     * Register custom open folder and close group actions that adjust the layout of the graph.
81     */
82    protected void registerViewActions() {
83      super.registerViewActions();
84      view.getCanvasComponent().getActionMap().put(Graph2DViewActions.CLOSE_GROUPS, new CloseGroupsAndLayoutAction());
85      view.getCanvasComponent().getActionMap().put(Graph2DViewActions.OPEN_FOLDERS, new OpenFoldersAndLayoutAction());
86    }
87  
88    /**
89     * Expand a folder node. After expanding the folder node, an incremental layout is automatically triggered.
90     * For this, the expanded node and all of its descendants will be treated as incremental elements.
91     */
92    class OpenFoldersAndLayoutAction extends Graph2DViewActions.OpenFoldersAction {
93  
94      OpenFoldersAndLayoutAction() {
95        super(IncrementalHierarchicGroupDemo.this.view);
96      }
97  
98      public void openFolder(Node folderNode, Graph2D graph) {
99        NodeList children = new NodeList(graph.getHierarchyManager().getInnerGraph(folderNode).nodes());
100       super.openFolder(folderNode, graph);
101       graph.unselectAll();
102       graph.setSelected(folderNode, true);
103       for (NodeCursor nc = children.nodes(); nc.ok(); nc.next()) {
104         graph.setSelected(nc.node(), true);
105       }
106 
107       layoutIncrementally();
108 
109       graph.unselectAll();
110       graph.setSelected(folderNode, true);
111       graph.updateViews();
112      
113     }
114   }
115 
116   /**
117    * Collapse a group node. After collapsing the group node, an incremental layout is automatically triggered.
118    * For this, the collapsed node is treated as an incremental element.
119    */
120   class CloseGroupsAndLayoutAction extends Graph2DViewActions.CloseGroupsAction {
121 
122     CloseGroupsAndLayoutAction() {
123       super(IncrementalHierarchicGroupDemo.this.view);
124     }
125 
126     public void closeGroup(Node groupNode, Graph2D graph) {
127       super.closeGroup(groupNode, graph);
128       graph.unselectAll();
129       graph.setSelected(groupNode, true);
130       for (EdgeCursor ec = groupNode.edges(); ec.ok(); ec.next()) {
131         graph.setSelected(ec.edge(), true);
132       }
133 
134       layoutIncrementally();
135       graph.unselectAll();
136 
137       graph.updateViews();      
138     }
139   }
140   
141   
142   /**
143    * Loads the initial graph
144    */
145   protected void loadInitialGraph() {
146     loadGraph("resource/IncrementalHierarchicGroupDemo.graphml");
147   }
148 
149   /**
150    * Creates the toolbar for the demo.
151    */
152   protected JToolBar createToolBar() {
153     JToolBar toolBar = super.createToolBar();    
154     addLayoutActions(toolBar);    
155     return toolBar;
156   }
157 
158   protected void addLayoutActions(JToolBar toolBar) {
159     final Action incrementalLayoutAction = new AbstractAction(
160             "Incremental", SHARED_LAYOUT_ICON) {
161       public void actionPerformed(ActionEvent e) {
162         layoutIncrementally();
163       }
164     };
165 
166     final Action layoutAction = new AbstractAction(
167             "Complete", SHARED_LAYOUT_ICON) {
168       public void actionPerformed(ActionEvent e) {
169         layout();
170       }
171     };
172 
173     final Action propertiesAction = new AbstractAction(
174             "Settings...", getIconResource("resource/properties.png")) {
175       public void actionPerformed(ActionEvent e) {
176         final ActionListener layoutListener = new ActionListener() {
177           public void actionPerformed(ActionEvent e) {
178             layout();
179           }
180         };
181         OptionSupport.showDialog(groupLayoutOptions, layoutListener, false, view.getFrame());
182         configureGroupLayout();
183       }
184     };
185 
186     toolBar.addSeparator();
187     toolBar.add(new JLabel("Layout: "));
188     toolBar.add(createActionControl(incrementalLayoutAction));
189     toolBar.add(createActionControl(layoutAction));
190     toolBar.add(createActionControl(propertiesAction));
191   }
192 
193   /**
194    * Configures the layouter options relevant for grouping.
195    */
196   void configureGroupLayout() {
197     Object gsi = groupLayoutOptions.get("Group Layering Strategy");
198     if ("Recursive Layering".equals(gsi)) {
199       layouter.setRecursiveGroupLayeringEnabled(true);
200     } else if ("Global Layering".equals(gsi)) {
201       layouter.setRecursiveGroupLayeringEnabled(false);
202     }
203 
204     layouter.setGroupCompactionEnabled(groupLayoutOptions.getBool("Enable Compact Layering"));
205 
206     Object gai = groupLayoutOptions.get("Group Alignment");
207     if ("Top".equals(gai)) {
208       layouter.setGroupAlignmentPolicy(IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_TOP);
209     } else if ("Center".equals(gai)) {
210       layouter.setGroupAlignmentPolicy(IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_CENTER);
211     }
212     if ("Bottom".equals(gai)) {
213       layouter.setGroupAlignmentPolicy(IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_BOTTOM);
214     }
215   }
216 
217   /**
218    * Performs incremental layout. All selected elements will be treated incrementally.
219    */
220   void layoutIncrementally() {
221     Graph2D graph = view.getGraph2D();
222 
223     layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
224 
225     // create storage for both nodes and edges
226     DataMap incrementalElements = Maps.createHashedDataMap();
227     // configure the mode
228     final IncrementalHintsFactory ihf = layouter.createIncrementalHintsFactory();
229 
230     for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
231       incrementalElements.set(nc.node(), ihf.createLayerIncrementallyHint(nc.node()));
232     }
233 
234     for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
235       incrementalElements.set(ec.edge(), ihf.createSequenceIncrementallyHint(ec.edge()));
236     }
237     graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, incrementalElements);
238     try {
239       final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor();
240       layoutExecutor.getLayoutMorpher().setSmoothViewTransform(true);
241       layoutExecutor.doLayout(view, layouter);
242     } finally {
243       graph.removeDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY);
244     }
245   }
246 
247   /**
248    * Performs global layout. The new layout can strongly differ from the existing layout.
249    */
250   void layout() {
251     layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
252     final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor();
253     layoutExecutor.getLayoutMorpher().setSmoothViewTransform(true);
254     layoutExecutor.doLayout(view, layouter);
255   }
256 
257   /**
258    * Launches this demo.
259    * @param args ignored.
260    */
261   public static void main(String[] args) {
262     EventQueue.invokeLater(new Runnable() {
263       public void run() {
264         Locale.setDefault(Locale.ENGLISH);
265         initLnF();
266         (new IncrementalHierarchicGroupDemo()).start();
267       }
268     });
269   }
270 }
271