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.tree;
15  
16  import y.base.Node;
17  import y.base.NodeCursor;
18  import y.layout.tree.AbstractRotatableNodePlacer;
19  import y.layout.tree.AbstractRotatableNodePlacer.Matrix;
20  import y.layout.tree.AbstractRotatableNodePlacer.RootAlignment;
21  import y.layout.tree.BusPlacer;
22  import y.layout.tree.DoubleLinePlacer;
23  import y.layout.tree.LeftRightPlacer;
24  import y.layout.tree.NodePlacer;
25  import y.layout.tree.SimpleNodePlacer;
26  import y.view.Arrow;
27  import y.view.EdgeRealizer;
28  import y.view.Graph2D;
29  import y.view.Graph2DSelectionEvent;
30  import y.view.Graph2DSelectionListener;
31  import y.view.LineType;
32  import y.view.PolyLineEdgeRealizer;
33  
34  import javax.swing.DefaultListCellRenderer;
35  import javax.swing.JButton;
36  import javax.swing.JComboBox;
37  import javax.swing.JLabel;
38  import javax.swing.JList;
39  import javax.swing.JPanel;
40  import javax.swing.JSplitPane;
41  import java.awt.BorderLayout;
42  import java.awt.Component;
43  import java.awt.FlowLayout;
44  import java.awt.GridBagConstraints;
45  import java.awt.GridBagLayout;
46  import java.awt.Insets;
47  import java.awt.EventQueue;
48  import java.awt.event.ActionEvent;
49  import java.awt.event.ActionListener;
50  import java.awt.event.ItemEvent;
51  import java.awt.event.ItemListener;
52  import java.util.Locale;
53  
54  /**
55   * This demo presents GenericTreeLayouter in conjunction with {@link NodePlacer}s that support
56   * subtree rotation. The NodePlacers, rotations and root alignments for the selected nodes may
57   * be changed using the panel on the left side.
58   *
59   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/cls_GenericTreeLayouter.html">Section Generic Tree Layout</a> in the yFiles for Java Developer's Guide
60   **/
61  public class RotatableNodePlacersDemo extends AbstractTreeDemo {
62    private JComboBox nodePlacerCombo;
63    private JComboBox rootAlignmentCombo;
64    private JButton rotLeftButton;
65    private JButton rotRightButton;
66    private JButton mirHorButton;
67    private JButton mirVertButton;
68  
69    public static void main(String[] args) {
70      EventQueue.invokeLater(new Runnable() {
71        public void run() {
72          Locale.setDefault(Locale.ENGLISH);
73          initLnF();
74          (new RotatableNodePlacersDemo()).start();
75        }
76      });
77    }
78  
79    public RotatableNodePlacersDemo() {
80      Graph2D graph = view.getGraph2D();
81  
82      graph.addGraph2DSelectionListener(new Graph2DSelectionListener() {
83        public void onGraph2DSelectionEvent(Graph2DSelectionEvent e) {
84          if (view.getGraph2D().selectedNodes().ok()) {
85            readComboValues();
86          } else {
87            setEnabled(false);
88          }
89        }
90      });
91  
92      //Realizers
93      EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
94      defaultER.setArrow(Arrow.STANDARD);
95      ((PolyLineEdgeRealizer) defaultER).setSmoothedBends(true);
96      defaultER.setLineType(LineType.LINE_2);
97  
98  
99      JPanel configPanel = new JPanel(new GridBagLayout());
100     GridBagConstraints constraints = new GridBagConstraints();
101 
102     constraints.fill = GridBagConstraints.HORIZONTAL;
103     constraints.insets = new Insets(10, 10, 10, 10);
104     configPanel.add(new JLabel("Settings for actual selection"), constraints);
105 
106     constraints.gridy = 1;
107     constraints.insets = new Insets(5, 5, 0, 0);
108     configPanel.add(new JLabel("NodePlacer:"), constraints);
109 
110     constraints.gridy = 2;
111     constraints.insets = new Insets(0, 0, 0, 0);
112     nodePlacerCombo = new JComboBox();
113     nodePlacerCombo.addItem("SimpleNodePlacer");
114     nodePlacerCombo.addItem("DoubleLinePlacer");
115     nodePlacerCombo.addItem("BusPlacer");
116     nodePlacerCombo.addItem("LeftRightPlacer");
117     nodePlacerCombo.addItemListener(new ItemListener() {
118       public void itemStateChanged(ItemEvent e) {
119         if (e.getStateChange() == ItemEvent.SELECTED) {
120           changeNodePlacersForSelection();
121         }
122       }
123     });
124     configPanel.add(nodePlacerCombo, constraints);
125 
126     constraints.gridy = 3;
127     constraints.insets = new Insets(5, 5, 0, 0);
128     configPanel.add(new JLabel("Rotation:"), constraints);
129 
130     constraints.gridy = 4;
131     constraints.insets = new Insets(0, 0, 0, 0);
132 
133     JPanel rotationPanel = new JPanel();
134     configPanel.add(rotationPanel, constraints);
135     rotationPanel.setLayout(new FlowLayout());
136     rotLeftButton = new JButton("Left");
137     rotLeftButton.addActionListener(new ActionListener() {
138       public void actionPerformed(ActionEvent e) {
139         rotate(Matrix.ROT90);
140       }
141     });
142     rotationPanel.add(rotLeftButton);
143     rotRightButton = new JButton("Right");
144     rotRightButton.addActionListener(new ActionListener() {
145       public void actionPerformed(ActionEvent e) {
146         rotate(Matrix.ROT270);
147       }
148     });
149     rotationPanel.add(rotRightButton);
150 
151     constraints.gridy = 6;
152     rotationPanel = new JPanel();
153     configPanel.add(rotationPanel, constraints);
154     rotationPanel.setLayout(new FlowLayout());
155     mirHorButton = new JButton("Mir Hor");
156     mirHorButton.addActionListener(new ActionListener() {
157       public void actionPerformed(ActionEvent e) {
158         rotate(Matrix.MIR_HOR);
159       }
160     });
161     rotationPanel.add(mirHorButton);
162     mirVertButton = new JButton("Mir Vert");
163     mirVertButton.addActionListener(new ActionListener() {
164       public void actionPerformed(ActionEvent e) {
165         rotate(Matrix.MIR_VERT);
166       }
167     });
168     rotationPanel.add(mirVertButton);
169 
170 
171     constraints.gridy = 7;
172     constraints.insets = new Insets(5, 5, 0, 0);
173     configPanel.add(new JLabel("Root Alignment:"), constraints);
174 
175     constraints.gridy = 8;
176     constraints.insets = new Insets(0, 0, 0, 0);
177     rootAlignmentCombo = new JComboBox();
178 
179     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.CENTER);
180     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.CENTER_OVER_CHILDREN);
181     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.MEDIAN);
182     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.LEADING);
183     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.LEFT);
184     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.RIGHT);
185     rootAlignmentCombo.addItem(AbstractRotatableNodePlacer.RootAlignment.TRAILING);
186     rootAlignmentCombo.addItemListener(new ItemListener() {
187       public void itemStateChanged(ItemEvent e) {
188         if (e.getStateChange() == ItemEvent.SELECTED) {
189           changeNodePlacersForSelection();
190         }
191       }
192     });
193     rootAlignmentCombo.setRenderer(new DefaultListCellRenderer() {
194       public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
195                                                     boolean cellHasFocus) {
196         JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
197         RootAlignment rootAlignment = (RootAlignment) value;
198         if (rootAlignment == RootAlignment.CENTER) {
199           label.setText("Center");
200         }
201         if (rootAlignment == RootAlignment.LEADING) {
202           label.setText("Leading");
203         }
204         if (rootAlignment == RootAlignment.LEFT) {
205           label.setText("Left");
206         }
207         if (rootAlignment == RootAlignment.RIGHT) {
208           label.setText("Right");
209         }
210         if (rootAlignment == RootAlignment.TRAILING) {
211           label.setText("Trailing");
212         }
213         if (rootAlignment == RootAlignment.MEDIAN) {
214           label.setText("Median");
215         }
216         if (rootAlignment == RootAlignment.CENTER_OVER_CHILDREN) {
217           label.setText("Center over children");
218         }
219         return label;
220       }
221     });
222     configPanel.add(rootAlignmentCombo, constraints);
223 
224     JPanel left = new JPanel(new BorderLayout());
225     left.add(configPanel, BorderLayout.NORTH);
226 
227     JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, view);
228     sp.setOneTouchExpandable(true);
229     sp.setContinuousLayout(false);
230     contentPane.add(sp, BorderLayout.CENTER);
231     createSampleGraph(graph);
232 
233     setEnabled(false);
234 
235     createSampleGraph(view.getGraph2D());
236     calcLayout();
237   }
238 
239   private void setEnabled(boolean enabled) {
240     rootAlignmentCombo.setEnabled(enabled);
241     rotLeftButton.setEnabled(enabled);
242     rotRightButton.setEnabled(enabled);
243     mirHorButton.setEnabled(enabled);
244     mirVertButton.setEnabled(enabled);
245     nodePlacerCombo.setEnabled(enabled);
246   }
247 
248   private void rotate(Matrix rotation) {
249     RootAlignment rootAlignment = (RootAlignment) rootAlignmentCombo.getSelectedItem();
250 
251     String placerName = (String) nodePlacerCombo.getSelectedItem();
252 
253     for (NodeCursor nodeCursor = view.getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next()) {
254       Node node = nodeCursor.node();
255       AbstractRotatableNodePlacer oldPlacer = (AbstractRotatableNodePlacer) nodePlacerMap.get(node);
256       Matrix matrix = oldPlacer == null ? rotation.multiply(Matrix.DEFAULT) : rotation.multiply(
257           oldPlacer.getModificationMatrix());
258 
259       AbstractRotatableNodePlacer placer = createPlacer(placerName, matrix, rootAlignment);
260       nodePlacerMap.set(node, placer);
261     }
262     calcLayout();
263   }
264 
265   private boolean blockLayout;
266 
267   private void readComboValues() {
268     blockLayout = true;
269 
270     setEnabled(true);
271 
272     NodeCursor nodeCursor = view.getGraph2D().selectedNodes();
273     if (!nodeCursor.ok()) {
274       setEnabled(false);
275     } else {
276       Node node = nodeCursor.node();
277 
278       AbstractRotatableNodePlacer nodePlacer = (AbstractRotatableNodePlacer) nodePlacerMap.get(node);
279       if (nodePlacer == null) {
280         return;
281       }
282 
283       nodePlacerCombo.setSelectedItem(nodePlacer.getClass());
284 
285       if (nodePlacer instanceof SimpleNodePlacer) {
286         rootAlignmentCombo.setSelectedItem(((SimpleNodePlacer) nodePlacer).getRootAlignment());
287       }
288       if (nodePlacer instanceof DoubleLinePlacer) {
289         rootAlignmentCombo.setSelectedItem(((DoubleLinePlacer) nodePlacer).getRootAlignment());
290       }
291     }
292 
293     blockLayout = false;
294   }
295 
296   private void changeNodePlacersForSelection() {
297     if (blockLayout) {
298       return;
299     }
300     RootAlignment rootAlignment = (RootAlignment) rootAlignmentCombo.getSelectedItem();
301 
302     String placerName = (String) nodePlacerCombo.getSelectedItem();
303 
304     for (NodeCursor nodeCursor = view.getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next()) {
305       Node node = nodeCursor.node();
306       AbstractRotatableNodePlacer oldPlacer = (AbstractRotatableNodePlacer) nodePlacerMap.get(node);
307       Matrix matrix = oldPlacer != null ? oldPlacer.getModificationMatrix() : AbstractRotatableNodePlacer.Matrix.DEFAULT;
308 
309       AbstractRotatableNodePlacer placer = createPlacer(placerName, matrix, rootAlignment);
310       nodePlacerMap.set(node, placer);
311     }
312     calcLayout();
313   }
314 
315   private static AbstractRotatableNodePlacer createPlacer(String placerName, Matrix modificationMatrix,
316                                                           RootAlignment rootAlignment) {
317     AbstractRotatableNodePlacer placer = null;
318     if ("SimpleNodePlacer".equals(placerName)) {
319       placer = new SimpleNodePlacer(modificationMatrix);
320       ((SimpleNodePlacer) placer).setRootAlignment(rootAlignment);
321     } else if ("DoubleLinePlacer".equals(placerName)) {
322       placer = new DoubleLinePlacer(modificationMatrix);
323       ((DoubleLinePlacer) placer).setRootAlignment(rootAlignment);
324     } else if ("BusPlacer".equals(placerName)) {
325       placer = new BusPlacer(modificationMatrix);
326     } else if ("LeftRightPlacer".equals(placerName)) {
327       placer = new LeftRightPlacer(modificationMatrix);
328     }
329     return placer;
330   }
331 
332   protected boolean isDeletionEnabled() {
333     return false;
334   }
335 
336   private void createSampleGraph(Graph2D graph) {
337     graph.clear();
338     Node root = graph.createNode();
339     graph.getRealizer(root).setFillColor(layerColors[0]);
340     nodePlacerMap.set(root, new SimpleNodePlacer());
341     createChildren(graph, root, 3, 1, 2);
342     calcLayout();
343   }
344 
345   private void createChildren(Graph2D graph, Node root, int children, int layer, int layers) {
346     for (int i = 0; i < children; i++) {
347       Node child = graph.createNode();
348       graph.createEdge(root, child);
349       graph.getRealizer(child).setFillColor(layerColors[layer % layerColors.length]);
350 
351       Matrix rotationMatrix;
352       if (layer == 1) {
353         rotationMatrix = Matrix.MIR_VERT_ROT90;
354       } else {
355         rotationMatrix = Matrix.DEFAULT;
356       }
357       if (layers > 0) {
358         SimpleNodePlacer nodePlacer = new SimpleNodePlacer(rotationMatrix);
359         nodePlacerMap.set(child, nodePlacer);
360       }
361       if (layers > 0) {
362         createChildren(graph, child, children, layer + 1, layers - 1);
363       }
364     }
365   }
366 }
367