1
28 package demo.layout;
29
30 import demo.view.DemoBase;
31 import demo.view.DemoDefaults;
32 import y.algo.GraphConnectivity;
33 import y.algo.AbortHandler;
34 import y.algo.AlgorithmAbortedException;
35 import y.base.Node;
36 import y.base.NodeCursor;
37 import y.base.NodeList;
38 import y.layout.BufferedLayouter;
39 import y.layout.Layouter;
40 import y.layout.hierarchic.IncrementalHierarchicLayouter;
41 import y.layout.organic.SmartOrganicLayouter;
42 import y.layout.orthogonal.OrthogonalLayouter;
43 import y.layout.random.RandomLayouter;
44 import y.view.Graph2D;
45 import y.view.Graph2DLayoutExecutor;
46
47 import javax.swing.AbstractAction;
48 import javax.swing.Action;
49 import javax.swing.BorderFactory;
50 import javax.swing.Box;
51 import javax.swing.JButton;
52 import javax.swing.JComboBox;
53 import javax.swing.JDialog;
54 import javax.swing.JLabel;
55 import javax.swing.JOptionPane;
56 import javax.swing.JPanel;
57 import javax.swing.JProgressBar;
58 import javax.swing.JRootPane;
59 import javax.swing.JToolBar;
60 import javax.swing.SwingUtilities;
61 import java.awt.BorderLayout;
62 import java.awt.Dimension;
63 import java.awt.EventQueue;
64 import java.awt.event.ActionEvent;
65 import java.util.Arrays;
66 import java.util.Comparator;
67 import java.util.Locale;
68 import java.util.Random;
69
70
76 public class Graph2DLayoutExecutorDemo extends DemoBase
77 {
78 private JLabel statusLabel;
80 private JProgressBar progressBar = new JProgressBar();
82
83 private JComboBox layoutExecutionTypeBox;
85
86 private JComboBox layouterBox;
88
89 public Graph2DLayoutExecutorDemo() {
90 buildGraph( view.getGraph2D() );
92
93 view.setViewPoint2D(-200.0, -200.0);
94 }
95
96 protected void configureDefaultRealizers() {
97 DemoDefaults.registerDefaultNodeConfiguration(false);
100 DemoDefaults.configureDefaultRealizers(view);
101 }
102
103
106 public void addContentTo(JRootPane rootPane) {
107 this.statusLabel = new JLabel("Status");
108 final Dimension minimumSize = this.statusLabel.getMinimumSize();
109 this.statusLabel.setMinimumSize(new Dimension(Math.max(200, minimumSize.width), minimumSize.height));
110 final JPanel panel = new JPanel();
111 panel.add(this.statusLabel, BorderLayout.LINE_START);
112 this.progressBar.setMaximum(100);
113 this.progressBar.setMinimum(0);
114 this.progressBar.setValue(0);
115 panel.add(progressBar, BorderLayout.CENTER);
116 getContentPane().add(panel, BorderLayout.SOUTH);
117 super.addContentTo(rootPane);
118 }
119
120
121 void buildGraph(Graph2D graph) {
122 graph.clear();
123 Node[] nodes = new Node[800];
124 for(int i = 0; i < nodes.length; i++)
125 {
126 nodes[i] = graph.createNode();
127 }
128 Random random = new Random(0L);
129 for ( int i = 0; i < nodes.length; i++ ) {
130
131 int edgeCount;
132
133 if (random.nextInt(10) == 0) {
134 edgeCount = 4 + random.nextInt(5);
135 } else {
136 edgeCount = random.nextInt(3);
137 }
138
139 for ( int j = 0; j < edgeCount; j++ ) {
140 graph.createEdge( nodes[ i ], nodes[ random.nextInt(nodes.length) ] );
141 }
142 }
143
144 final NodeList[] components = GraphConnectivity.connectedComponents(graph);
146 Arrays.sort(components, new Comparator() {
147 public int compare(final Object o1, final Object o2) {
148 return ((NodeList) o2).size() - ((NodeList) o1).size();
149 }
150 });
151 for (int i = components.length -1; i > 0; i--) {
152 for (NodeCursor nc = components[i].nodes(); nc.ok(); nc.next()) {
153 graph.removeNode(nc.node());
154 }
155 }
156
157 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
158 final Node node = nc.node();
159 graph.getRealizer(node).setLabelText(Integer.toString(node.index()));
160 }
161
162 (new BufferedLayouter(new RandomLayouter())).doLayout(graph);
163 }
164
165
168 protected JToolBar createToolBar() {
169 final Action layoutAction = new AbstractAction(
170 "Layout", SHARED_LAYOUT_ICON) {
171 public void actionPerformed(ActionEvent e) {
172 applyLayout();
173 }
174 };
175
176 layouterBox = new JComboBox(new Object[]{"Hierarchic", "Organic", "Orthogonal"});
178 layouterBox.setMaximumSize(layouterBox.getPreferredSize());
179 layouterBox.setSelectedIndex(0);
180
181 layoutExecutionTypeBox = new JComboBox(
183 new Object[]{"Animated", "AnimatedThreaded", "Buffered", "Threaded", "Unbuffered", "AnimatedInOwnThread"});
184 layoutExecutionTypeBox.setMaximumSize(layoutExecutionTypeBox.getPreferredSize());
185 layoutExecutionTypeBox.setSelectedIndex(1);
186
187 final JToolBar toolBar = super.createToolBar();
188 toolBar.addSeparator();
189 toolBar.add(createActionControl(layoutAction));
190 toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
191 toolBar.add(layouterBox);
192 toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
193 toolBar.add(layoutExecutionTypeBox);
194
195 return toolBar;
196 }
197
198
201 void applyLayout() {
202 Layouter layouter = createLayouter();
203 switch (layoutExecutionTypeBox.getSelectedIndex()) {
204 case 0:
205 applyLayoutAnimated(layouter);
206 break;
207 case 1:
208 applyLayoutAnimatedThreaded(layouter);
209 break;
210 case 2:
211 applyLayoutBuffered(layouter);
212 break;
213 case 3:
214 applyLayoutThreaded(layouter);
215 break;
216 case 4:
217 applyLayoutUnbuffered(layouter);
218 break;
219 case 5:
220 applyLayoutAnimatedInOwnThread(layouter);
221 break;
222 }
223 }
224
225
228 Layouter createLayouter() {
229 switch (layouterBox.getSelectedIndex()) {
230 default:
231 case 0:
232 return new IncrementalHierarchicLayouter();
233 case 1:
234 final SmartOrganicLayouter organicLayouter = new SmartOrganicLayouter();
235 organicLayouter.setQualityTimeRatio(1.0);
236 organicLayouter.setMaximumDuration(2L * 60L * 1000L);
237 organicLayouter.setMultiThreadingAllowed(true);
238 return organicLayouter;
239 case 2:
240 return new OrthogonalLayouter();
241 }
242 }
243
244
250 void applyLayoutAnimatedThreaded(final Layouter layouter) {
251 this.progressBar.setIndeterminate(true);
252 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.ANIMATED_THREADED);
253 layoutExecutor.getLayoutMorpher().setPreferredDuration(3000L);
255 layoutExecutor.getLayoutMorpher().setEasedExecution(true);
256 layoutExecutor.getLayoutMorpher().setSmoothViewTransform(true);
257 layoutExecutor.setLockingView(true);
259
260 final AbortHandler handler = AbortHandler.createForGraph(view.getGraph2D());
261 handler.reset();
267
268 final JDialog dialog = newCancelDialog(handler, layouter.getClass().getName());
269
270 final Graph2DLayoutExecutor.LayoutThreadHandle handle = layoutExecutor.doLayout(view, layouter, new Runnable() {
273 public void run() {
274 dialog.dispose();
275 progressBar.setIndeterminate(false);
276 statusLabel.setText("Layout Done");
277 }
278 }, new ExceptionHandler());
279
280 this.statusLabel.setText("Layout is running");
282
283
284 if (handle.isRunning()) {
285 dialog.setVisible(true);
286 }
287 }
288
289
293 void applyLayoutBuffered(final Layouter layouter){
294 final AbortHandler handler = AbortHandler.createForGraph(view.getGraph2D());
295 handler.reset();
301
302 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.BUFFERED);
303 layoutExecutor.doLayout(view, layouter);
304 }
305
306
311 void applyLayoutAnimated(final Layouter layouter){
312 statusLabel.setText("Starting Animated Blocking Layout");
314 progressBar.setIndeterminate(true);
315
316 final AbortHandler handler = AbortHandler.createForGraph(view.getGraph2D());
317 handler.reset();
323
324 try {
325 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.ANIMATED);
326 layoutExecutor.doLayout(view, layouter);
327 } finally {
328 progressBar.setIndeterminate(false);
329 statusLabel.setText("Animated Blocking Layout Done.");
330 }
331 }
332
333
338 void applyLayoutAnimatedInOwnThread(final Layouter layouter){
339 statusLabel.setText("Starting own layout thread.");
340 progressBar.setIndeterminate(true);
341 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.ANIMATED);
342
343 final AbortHandler handler = AbortHandler.createForGraph(view.getGraph2D());
344 handler.reset();
350
351 final JDialog dialog = newCancelDialog(handler, layouter.getClass().getName());
352
353 new Thread(new Runnable() {
354 public void run() {
355 try {
356 layoutExecutor.doLayout(view, layouter, null, new ExceptionHandler());
357 } finally {
358 SwingUtilities.invokeLater(new Runnable() {
359 public void run() {
360 dialog.dispose();
361 statusLabel.setText("Layout Thread Finished.");
362 progressBar.setIndeterminate(false);
363 }
364 });
365 }
366 }
367 }).start();
368
369 dialog.setVisible(true);
370 }
371
372
376 void applyLayoutThreaded(final Layouter layouter){
377 statusLabel.setText("Starting threaded layout");
378 progressBar.setIndeterminate(true);
379 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.THREADED);
380
381 final AbortHandler handler = AbortHandler.createForGraph(view.getGraph2D());
382 handler.reset();
388
389 final JDialog dialog = newCancelDialog(handler, layouter.getClass().getName());
390
391 layoutExecutor.doLayout(view, layouter, new Runnable() {
392 public void run() {
393 dialog.dispose();
394 statusLabel.setText("Layout Returned");
395 progressBar.setIndeterminate(false);
396 }
397 }, new ExceptionHandler());
398 statusLabel.setText("Return from doLayout()");
399
400 dialog.setVisible(true);
401 }
402
403 void applyLayoutUnbuffered(final Layouter layouter) {
404 final AbortHandler handler = AbortHandler.createForGraph(view.getGraph2D());
405 handler.reset();
411
412 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.UNBUFFERED);
413 layoutExecutor.doLayout(view, layouter);
414 }
415
416
421 private JDialog newCancelDialog(
422 final AbortHandler handler, final String layoutName
423 ) {
424 final JDialog dialog = new JDialog(JOptionPane.getRootFrame(), "");
425 final Box box = Box.createVerticalBox();
426 box.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
427 final JLabel label = new JLabel("Layout Running [" + layoutName + "].");
428 box.add(label);
429 box.add(Box.createVerticalStrut(12));
430 box.add(new JButton(new AbstractAction("Stop") {
431 private boolean stopped;
432 public void actionPerformed(ActionEvent e) {
433 if (!stopped) {
435 handler.stop();
436 statusLabel.setText("Stopping.");
437 label.setText("Stopping Thread.[" + layoutName + "].");
438 ((JButton)e.getSource()).setText("Cancel");
439 stopped = true;
440 } else {
441 handler.cancel();
444 setEnabled(false);
445 statusLabel.setText("Cancelling.");
446 }
447 }
448 }));
449 dialog.getContentPane().add(box);
450 dialog.setLocationRelativeTo(view);
451 dialog.pack();
452 return dialog;
453 }
454
455 public static void main(String[] args) {
456 EventQueue.invokeLater(new Runnable() {
457 public void run() {
458 Locale.setDefault(Locale.ENGLISH);
459 initLnF();
460 (new Graph2DLayoutExecutorDemo()).start();
461 }
462 });
463 }
464
465
468 private class ExceptionHandler implements Graph2DLayoutExecutor.ExceptionListener {
469 public void exceptionHappened( final Throwable t ) {
470 if (t instanceof AlgorithmAbortedException) {
471 statusLabel.setText("Layout cancelled.");
472 } else {
473 t.printStackTrace(System.err);
474 statusLabel.setText("Exception Happened.");
475 }
476 }
477 }
478 }
479