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.Edge;
18  import y.base.EdgeCursor;
19  import y.base.EdgeMap;
20  import y.base.Node;
21  import y.base.NodeCursor;
22  import y.base.NodeMap;
23  import y.layout.BufferedLayouter;
24  import y.layout.GraphLayout;
25  import y.layout.hierarchic.ConstraintLayerer;
26  import y.layout.hierarchic.ConstraintLayerer.ConstraintFactory;
27  import y.layout.hierarchic.HierarchicLayouter;
28  import y.view.Arrow;
29  import y.view.EdgeRealizer;
30  import y.view.Graph2D;
31  import y.view.LayoutMorpher;
32  
33  import javax.swing.AbstractAction;
34  import javax.swing.BorderFactory;
35  import javax.swing.JButton;
36  import javax.swing.JPanel;
37  import javax.swing.JScrollPane;
38  import javax.swing.JToolBar;
39  import java.awt.BorderLayout;
40  import java.awt.Color;
41  import java.awt.GridBagConstraints;
42  import java.awt.GridBagLayout;
43  import java.awt.Insets;
44  import java.awt.event.ActionEvent;
45  import java.awt.event.ActionListener;
46  
47  /**
48   * Demo that shows how to use the {@link ConstraintLayerer} class.
49   * <p> With the buttons on the left side of the GUI,
50   * various constraints can be set on the currently selected nodes (either absolute top/bottom level or relative layering
51   * constraints). The "Top-most"/"Bottom-most" buttons set absolute layering constraints, whereas the other buttons assign
52   * relative layering constraints. The top button ("Remove constraints") clears all constraints from the currently
53   * selected nodes.
54   * </p>
55   * <p>
56   * Additionally, a DataProvider is registered under the key <code>ConstraintLayerer.EDGE_WEIGHTS_DPKEY</code>, and if a numeric edge label is set, that label gets set as
57   * value for that DataProvider.
58   * </p>
59   */
60  public class ConstraintLayererDemo extends DemoBase {
61  
62    public static final int TOP_LEVEL = 1;
63    public static final int BOTTOM_LEVEL = 5;
64  
65    private NodeMap levels;
66    private EdgeMap weights;
67  
68    public ConstraintLayererDemo() {
69      super();
70      final Graph2D graph = view.getGraph2D();
71      EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
72      defaultER.setArrow(Arrow.STANDARD);
73  
74      levels = graph.createNodeMap();
75  
76      //additionally, register an edge weight map
77      weights = graph.createEdgeMap();
78      graph.addDataProvider(ConstraintLayerer.EDGE_WEIGHTS_DPKEY, weights);
79  
80      JPanel left = new JPanel(new GridBagLayout());
81  
82      GridBagConstraints gbc = new GridBagConstraints();
83      gbc.fill = GridBagConstraints.BOTH;
84      gbc.anchor = GridBagConstraints.NORTHWEST;
85  
86      gbc.gridx = 0;
87      gbc.gridy = GridBagConstraints.RELATIVE;
88  
89      JPanel groupSpec;
90      groupSpec = new JPanel(new GridBagLayout());
91      groupSpec.setBorder(BorderFactory.createTitledBorder("Layering constraints"));
92  
93      // build the grouping mechanism
94  
95      Color[] groupColors = new Color[]{null, Color.RED, Color.ORANGE, Color.yellow, new Color(204, 255, 0), Color.GREEN};
96      String[] groupLabels = new String[]{"Remove constraint", "Top-most level", "Above medium level", "Medium level", "Below medium level",
97          "Bottom-most level"};
98      gbc.insets = new Insets(5, 0, 5, 0);
99      for (int i = 0; i < groupColors.length; i++) {
100       if(i > 1 && i < 5) {
101         gbc.insets = new Insets(0, 0, 0, 0);
102       }
103       else {
104         gbc.insets = new Insets(5, 0, 5, 0);
105       }
106       GroupButton groupButton = new GroupButton(groupColors[i], groupLabels[i], i);
107       groupSpec.add(groupButton, gbc);
108     }
109     gbc.weightx = gbc.weighty = 1;
110     groupSpec.add(new JPanel(), gbc);
111     gbc.weightx = gbc.weighty = 0;
112     gbc.gridwidth = 2;
113     left.add(groupSpec, gbc);
114     gbc.gridwidth = 1;
115 
116     gbc.weighty = 1.0d;
117     left.add(new JPanel(), gbc);
118 
119     contentPane.add(new JScrollPane(left), BorderLayout.WEST);
120   }
121 
122   protected JToolBar createToolBar() {
123     JToolBar bar = super.createToolBar();
124     bar.add(new LayoutAction());
125 //    bar.add(new OptionAction());
126     return bar;
127   }
128 
129   /** this method assigns the level id and the corresponding color hint to the currently selected nodes */
130   protected void assignLevel(Color color, int index) {
131     Graph2D graph = view.getGraph2D();
132 
133     for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
134       Node n = nc.node();
135       // set the color hint
136       if (color != null) {
137         graph.getRealizer(n).setFillColor(color);
138       } else {
139         graph.getRealizer(n).setFillColor(graph.getDefaultNodeRealizer().getFillColor());
140       }
141       levels.setInt(n, index);
142     }
143     graph.updateViews();
144   }
145 
146 
147   // helper class
148   class GroupButton extends JButton implements ActionListener {
149     Color color;
150     int index;
151 
152     GroupButton(Color color, String groupLabel, int index) {
153       super("");
154       setText(groupLabel);
155       this.color = color;
156       this.index = index;
157       setBackground(color);
158       this.addActionListener(this);
159     }
160 
161     public void actionPerformed(ActionEvent e) {
162       ConstraintLayererDemo.this.assignLevel(color, index);
163     }
164   }
165 
166   class LayoutAction extends AbstractAction {
167     LayoutAction() {
168       super("Layout");
169     }
170 
171     public void actionPerformed(ActionEvent ev) {
172       Graph2D graph = view.getGraph2D();
173 
174 
175       doLayout(graph);
176       graph.updateViews();
177       view.fitContent();
178     }
179   }
180 
181   private void doLayout(Graph2D graph) {
182     HierarchicLayouter hl = new HierarchicLayouter();
183     ConstraintFactory cf = ConstraintLayerer.createConstraintFactory(graph);
184     createConstraints(graph, cf);
185     ConstraintLayerer layerer = new ConstraintLayerer();
186     layerer.setAllowSameLayerEdges(false);
187     hl.setLayerer(layerer);
188 
189     GraphLayout gl = new BufferedLayouter(hl).calcLayout(graph);
190     new LayoutMorpher(view, gl).execute();
191 //    hl.doLayout(graph);
192     cf.dispose();
193   }
194 
195   /**
196    * Assign constraints to nodes and edges
197    *
198    * @param graph
199    * @param cf
200    */
201   private void createConstraints(Graph2D graph, ConstraintFactory cf) {
202     Node flr, slr, thrdlr;
203     flr = slr = thrdlr = null;
204     for (NodeCursor nodeCursor = graph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
205       Node node = nodeCursor.node();
206       int index = levels.getInt(node);
207       switch (index) {
208         case TOP_LEVEL:
209           cf.addPlaceNodeAtTopConstraint(node);
210           break;
211         case BOTTOM_LEVEL:
212           cf.addPlaceNodeAtBottomConstraint(node);
213           break;
214         case TOP_LEVEL + 1:
215           if (flr == null) {
216             flr = node;
217           } else {
218             cf.addPlaceNodeInSameLayerConstraint(flr, node);
219           }
220           break;
221         case TOP_LEVEL + 2:
222           if (slr == null) {
223             slr = node;
224           } else {
225             cf.addPlaceNodeInSameLayerConstraint(slr, node);
226           }
227           break;
228         case TOP_LEVEL + 3:
229           if (thrdlr == null) {
230             thrdlr = node;
231           } else {
232             cf.addPlaceNodeInSameLayerConstraint(thrdlr, node);
233           }
234           break;
235         default:
236           break;
237       }
238       if (flr != null && slr != null) {
239         //place second layer below first layer
240         cf.addPlaceNodeBelowConstraint(flr, slr);
241       }
242       if (slr != null && thrdlr != null) {
243         //place thrd layer below second layer
244         cf.addPlaceNodeBelowConstraint(slr, thrdlr);
245       }
246       if (flr != null && thrdlr != null) {
247         //place first layer above 3rd layer
248         cf.addPlaceNodeAboveConstraint(thrdlr, flr);
249       }
250     }
251     //assign weights from edge labels
252     for (EdgeCursor edgeCursor = graph.edges(); edgeCursor.ok(); edgeCursor.next()) {
253       Edge edge = edgeCursor.edge();
254       String str = graph.getLabelText(edge);
255       try {
256         weights.setInt(edge, Integer.parseInt(str));
257       }
258       catch (NumberFormatException e) {
259         weights.setInt(edge, 1);
260       }
261     }
262   }
263 
264 
265   /** Launches this demo. */
266   public static void main(String args[]) {
267     initLnF();
268     ConstraintLayererDemo demo = new ConstraintLayererDemo();
269 
270     demo.start("ConstraintLayerer Demo");
271   }
272 
273 
274 }
275