1
14 package demo.view.anim;
15
16 import y.io.GMLIOHandler;
17 import y.io.IOHandler;
18 import y.io.YGFIOHandler;
19 import y.option.CompoundEditor;
20 import y.option.ConstraintManager;
21 import y.option.DefaultEditorFactory;
22 import y.option.Editor;
23 import y.option.EnumOptionItem;
24 import y.option.GuiFactory;
25 import y.option.ItemEditor;
26 import y.option.OptionGroup;
27 import y.option.OptionHandler;
28 import y.option.OptionItem;
29 import y.util.D;
30 import y.view.Graph2D;
31 import y.view.Graph2DView;
32 import y.view.ViewAnimationFactory;
33
34 import javax.swing.AbstractAction;
35 import javax.swing.Action;
36 import javax.swing.BorderFactory;
37 import javax.swing.DefaultListCellRenderer;
38 import javax.swing.JButton;
39 import javax.swing.JComponent;
40 import javax.swing.JList;
41 import javax.swing.JPanel;
42 import javax.swing.JTable;
43 import javax.swing.ListCellRenderer;
44 import javax.swing.table.DefaultTableCellRenderer;
45 import javax.swing.table.TableCellRenderer;
46 import java.awt.BorderLayout;
47 import java.awt.Color;
48 import java.awt.Component;
49 import java.awt.FlowLayout;
50 import java.awt.GridBagConstraints;
51 import java.awt.GridBagLayout;
52 import java.awt.event.ActionEvent;
53 import java.io.IOException;
54 import java.net.URL;
55 import java.net.URLDecoder;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.Map;
59
60
66 abstract class AnimationEffectsDemoBase
67 {
68 static final byte NO_ANIM = (byte)0;
69
70 static final byte BLUR_IN = (byte)11;
71 static final byte FADE_IN = (byte)12;
72 static final byte IMPLODE = (byte)13;
73 static final byte WHIRL_IN = (byte)14;
74
75 static final byte EXTRACT = (byte)15;
76
77 static final byte BLUR_OUT = (byte)21;
78 static final byte FADE_OUT = (byte)22;
79 static final byte EXPLODE = (byte)23;
80 static final byte WHIRL_OUT = (byte)24;
81
82 static final byte RETRACT = (byte)25;
83
84 static final byte TRAVERSE_EDGE = (byte)31;
85 static final byte ZOOM = (byte)32;
86 static final byte MOVE_CAMERA = (byte)33;
87 static final byte MORPH = (byte)34;
88 static final byte RESIZE = (byte)35;
89 static final byte BLINK = (byte)36;
90 static final byte ANIMATED_LOAD = (byte)37;
91 static final byte ANIMATED_CLEAR = (byte)38;
92
93
94 static final String DEMO_NAME;
95 static
96 {
97 String name = AnimationEffectsDemo.class.getName();
98 name = name.substring(name.lastIndexOf('.')+1);
99 DEMO_NAME = name;
100 }
101
102
103 final Graph2DView view;
104 final GuiFactory i18n;
105 final OptionHandler oh;
106 boolean compoundAction;
107
108 private final boolean wantsRadioButtons;
109
110
115 AnimationEffectsDemoBase( final GuiFactory i18n,
116 final boolean wantsRadioButtons )
117 {
118 this.wantsRadioButtons = wantsRadioButtons;
119 this.view = new Graph2DView();
120 this.i18n = i18n;
121 this.oh = createOptionHandler();
122 this.compoundAction = false;
123 }
124
125 abstract void animate();
126
127 void selectAllEdges()
128 {
129 final Graph2D graph = view.getGraph2D();
130 graph.setSelected(graph.edges(), true);
131 }
132
133 void openGraph( final String resource )
134 {
135 final URL url = getClass().getResource(resource);
136 if (url != null)
137 {
138 final Graph2D graph = view.getGraph2D();
139 graph.clear();
140
141 try
142 {
143 final String name = URLDecoder.decode(url.getFile(), "UTF-8");
144 getIoHandlerByExt(name).read(graph, name);
145 }
146 catch (IOException ioe)
147 {
148 D.show(ioe);
149 }
150
151 view.fitContent();
152 }
153 else
154 {
155 final java.io.File file = new java.io.File(resource);
156 if (file.exists())
157 {
158 final Graph2D graph = view.getGraph2D();
159 graph.clear();
160
161 try
162 {
163 final String name = file.getAbsolutePath();
164 getIoHandlerByExt(name).read(graph, name);
165 }
166 catch (IOException ioe)
167 {
168 D.show(ioe);
169 }
170
171 view.fitContent();
172 }
173 else
174 {
175 try
176 {
177 throw new Exception("Cannot locate file: " + resource);
178 }
179 catch (Exception ex)
180 {
181 D.show(ex);
182 }
183 }
184 }
185 }
186
187 void localizeAction( final Action action, final String key )
188 {
189 action.putValue(Action.NAME, i18n.getString(key));
190 action.putValue(Action.SHORT_DESCRIPTION,
191 i18n.getString(key + ".shortDescription"));
192 final java.net.URL iconUrl =
193 getClass().getResource(i18n.getString(key + ".smallIcon"));
194 if (iconUrl != null)
195 {
196 action.putValue(Action.SMALL_ICON, new javax.swing.ImageIcon(iconUrl));
197 }
198 }
199
200
201
204 private OptionHandler createOptionHandler()
205 {
206 final OptionHandler oh = new OptionHandler(DEMO_NAME);
207
208 final ConstraintManager cm = new ConstraintManager(oh);
209
210 OptionGroup group;
211 OptionGroup section;
212 OptionItem item;
213
214
215 final Byte noAnim = new Byte(NO_ANIM);
216
217
218 oh.useSection("elements");
219 section = new OptionGroup();
220 section.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "elements");
221
222 final Byte blurIn = new Byte(BLUR_IN);
223 final Byte fadeIn = new Byte(FADE_IN);
224 final Byte implode = new Byte(IMPLODE);
225 final Byte whirlIn = new Byte(WHIRL_IN);
226
227 final Byte blurOut = new Byte(BLUR_OUT);
228 final Byte fadeOut = new Byte(FADE_OUT);
229 final Byte explode = new Byte(EXPLODE);
230 final Byte whirlOut = new Byte(WHIRL_OUT);
231
232 final Byte extract = new Byte(EXTRACT);
233 final Byte retract = new Byte(RETRACT);
234
235 final ElementsRenderer elementsRenderer = new ElementsRenderer();
236 item = oh.addEnum("createNode", new Byte[] {noAnim, blurIn, fadeIn, implode, whirlIn}, 4);
237 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER, elementsRenderer);
238 section.addItem(item);
239 item = oh.addEnum("deleteNode", new Byte[] {noAnim, blurOut, fadeOut, explode, whirlOut}, 1);
240 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER, elementsRenderer);
241 section.addItem(item);
242 item = oh.addEnum("createEdge", new Byte[] {noAnim, blurIn, fadeIn, implode, extract}, 2);
243 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER, elementsRenderer);
244 section.addItem(item);
245 item = oh.addEnum("deleteEdge", new Byte[] {noAnim, blurOut, fadeOut, explode, retract}, 4);
246 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER, elementsRenderer);
247 section.addItem(item);
248
249 oh.useSection("misc");
250 section = new OptionGroup();
251
252 final Byte animatedClear = new Byte(ANIMATED_CLEAR);
253 final Byte animatedLoad = new Byte(ANIMATED_LOAD);
254 final Byte traverseEdge = new Byte(TRAVERSE_EDGE);
255 final Byte zoom = new Byte(ZOOM);
256 final Byte moveCamera = new Byte(MOVE_CAMERA);
257 final Byte morph = new Byte(MORPH);
258 final Byte resize = new Byte(RESIZE);
259 final Byte blink = new Byte(BLINK);
260
261 item = oh.addEnum("animation", new Byte[]{
262 animatedLoad, animatedClear,
263 traverseEdge, zoom, moveCamera,
264 morph, resize, blink
265 }, 2);
266 item.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
267 DEMO_NAME + "Base.animation");
268 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER,
269 new AnimationRenderer());
270 item.setAttribute(DefaultEditorFactory.ATTRIBUTE_ENUM_STYLE,
271 wantsRadioButtons
272 ? DefaultEditorFactory.STYLE_RADIO_BUTTONS
273 : DefaultEditorFactory.STYLE_COMBO_BOX);
274 section.addItem(item);
275
276 final OrderRenderer orderRenderer = new OrderRenderer();
277 final Object[] order = {
278 ViewAnimationFactory.LEFT_TO_RIGHT,
279 ViewAnimationFactory.RIGHT_TO_LEFT,
280 ViewAnimationFactory.CLOCKWISE,
281 ViewAnimationFactory.COUNTER_CLOCKWISE
282 };
283
284 group = new OptionGroup();
285 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
286 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
287 DEMO_NAME + "Base.animation");
288 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, animatedLoad.toString());
289 item = oh.addEnum("animateLoad_graph", new String[]{"big", "small"}, 0);
290 item.setAttribute(DefaultEditorFactory.ATTRIBUTE_ENUM_STYLE,
291 DefaultEditorFactory.STYLE_RADIO_BUTTONS);
292 group.addItem(item);
293 item = oh.addEnum("animateLoad_nodeOrder", order, 0);
294 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER, orderRenderer);
295 group.addItem(item);
296 group.addItem(oh.addBool("animateLoad_obeyEdgeDirection", false));
297 group.addItem(oh.addDouble("animateLoad_ratio", 0.15, 0.1, 1.0));
298 cm.setEnabledOnValueEquals("animation", animatedLoad, group);
299
300 group = new OptionGroup();
301 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
302 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
303 DEMO_NAME + "Base.animation");
304 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, animatedClear.toString());
305 item = oh.addEnum("animateClear_nodeOrder", order, 0);
306 item.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER, orderRenderer);
307 group.addItem(item);
308 group.addItem(oh.addBool("animateClear_obeyEdgeDirection", false));
309 group.addItem(oh.addDouble("animateClear_ratio", 0.15, 0.1, 1.0));
310 cm.setEnabledOnValueEquals("animation", animatedClear, group);
311
312 group = new OptionGroup();
313 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
314 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
315 DEMO_NAME + "Base.animation");
316 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, traverseEdge.toString());
317
318 group.addItem(oh.addColor("colorVisited", Color.RED, true, true, false, false));
319 group.addItem(oh.addColor("colorUnvisited", Color.BLACK, true, true, false, false));
320 cm.setEnabledOnValueEquals("animation", traverseEdge, group);
321 addItems(group, section);
322
323 group = new OptionGroup();
324 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
325 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
326 DEMO_NAME + "Base.animation");
327 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, zoom.toString());
328
329 group.addItem(oh.addDouble("zoom_factor", 1.0, 0.1, 16.0));
330 cm.setEnabledOnValueEquals("animation", zoom, group);
331
332 group = new OptionGroup();
333 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
334 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
335 DEMO_NAME + "Base.animation");
336 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, morph.toString());
337
338 group.addItem(oh.addDouble("translateX", 50));
339 group.addItem(oh.addDouble("translateY", 50));
340 group.addItem(oh.addDouble("width", 100));
341 group.addItem(oh.addDouble("height", 25));
342 group.addItem(oh.addColor("fillColor", Color.RED, true, true, false, false));
343 group.addItem(oh.addColor("fillColor2", Color.GREEN, true, true, false, false));
344 group.addItem(oh.addColor("lineColor", Color.BLACK, true, true, false, false));
345 cm.setEnabledOnValueEquals("animation", morph, group);
346 addItems(group, section);
347
348 group = new OptionGroup();
349 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
350 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
351 DEMO_NAME + "Base.animation");
352 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, resize.toString());
353
354 group.addItem(oh.addDouble("resize_width", 100));
355 group.addItem(oh.addDouble("resize_height", 100));
356 cm.setEnabledOnValueEquals("animation", resize, group);
357 addItems(group, section);
358
359 group = new OptionGroup();
360 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "misc_animation_properties");
361 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID,
362 DEMO_NAME + "Base.animation");
363 group.setAttribute(DefaultEditorFactory.ATTRIBUTE_CARD_ID, blink.toString());
364
365 group.addItem(oh.addInt("repetitions", 1, 1, 25));
366 cm.setEnabledOnValueEquals("animation", blink, group);
367 addItems(group, section);
368
369 oh.useSection("global");
370 oh.addDouble("speed", 1.0, 0.25, 4.0, 2);
371
372 return oh;
373 }
374
375 private JComponent createControlPane()
376 {
377 final DefaultEditorFactory editorFactory = new DefaultEditorFactory();
378 editorFactory.setGuiFactory(i18n);
379
380 final Map attributes = new HashMap();
381 final Editor editor = editorFactory.createEditor(oh, attributes);
382 setAutoAdopt(true, editor);
383 setAutoCommit(true, editor);
384
385 final JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.LEADING));
386 buttonPane.add(new JButton(createAnimateAction()));
387
388 JPanel spacer = new JPanel();
389 final JPanel pane = new JPanel(new GridBagLayout());
390
391 int row = 0;
392
393 final GridBagConstraints gbc = new GridBagConstraints();
394 gbc.fill = GridBagConstraints.HORIZONTAL;
395 gbc.anchor = GridBagConstraints.WEST;
396
397 {
398 final CompoundEditor ce = (CompoundEditor)editor;
399 for (int i = 0, n = ce.editorCount()-2; i < n; ++i)
400 {
401 gbc.gridy = row++;
402 pane.add(ce.getEditor(i).getComponent(), gbc);
403 }
404 {
407 final GridBagConstraints compoundConstraints = new GridBagConstraints();
408 final JPanel compoundPane = new JPanel(new GridBagLayout());
409 compoundPane.setBorder(
410 BorderFactory.createTitledBorder(
411 i18n.getString(DEMO_NAME + ".GROUP.misc")));
412
413 compoundConstraints.fill = GridBagConstraints.HORIZONTAL;
414 compoundConstraints.anchor = GridBagConstraints.NORTHWEST;
415 compoundConstraints.gridy = 0;
416 compoundConstraints.gridwidth = 2;
417 compoundConstraints.weightx = 1.0;
418 compoundPane.add(ce.getEditor(ce.editorCount()-2).getComponent(),
419 compoundConstraints);
420
421 compoundConstraints.fill = GridBagConstraints.BOTH;
422 compoundConstraints.anchor = GridBagConstraints.WEST;
423 compoundConstraints.gridy = 1;
424 compoundConstraints.weighty = 1.0;
425 compoundPane.add(spacer, compoundConstraints);
426
427 compoundConstraints.gridy = 2;
428 compoundConstraints.gridwidth = 1;
429 compoundConstraints.anchor = GridBagConstraints.SOUTHWEST;
430 compoundPane.add(buttonPane, compoundConstraints);
431
432 compoundConstraints.gridx = 1;
433 compoundPane.add(ce.getEditor(ce.editorCount()-1).getComponent(),
434 compoundConstraints);
435
436 gbc.gridy = row++;
437 pane.add(compoundPane, gbc);
438 }
439 }
440
441 spacer = new JPanel();
442 gbc.fill = GridBagConstraints.BOTH;
443 gbc.gridy = row;
444 gbc.weighty = 0.75;
445 pane.add(spacer, gbc);
446
447 return pane;
448 }
449
450 JComponent createContentPane()
451 {
452 final JPanel pane = new JPanel(new BorderLayout());
453 pane.add(createControlPane(), BorderLayout.WEST);
454 pane.add(view, BorderLayout.CENTER);
455 return pane;
456 }
457
458 private Action createAnimateAction()
459 {
460 final Action action = new AbstractAction()
461 {
462 public void actionPerformed( final ActionEvent e )
463 {
464 animate();
465 }
466 };
467 localizeAction(action, DEMO_NAME + ".action.Animate");
468
469 return action;
470 }
471
472 private IOHandler getIoHandlerByExt( final String filename )
473 {
474 if (filename.endsWith(".gml"))
475 {
476 return new GMLIOHandler();
477 }
478 else if (filename.endsWith(".ygf"))
479 {
480 return new YGFIOHandler();
481 }
482 else
483 {
484 throw new IllegalArgumentException("Unsupported file format: " + filename);
485 }
486 }
487
488
489 private static void addItems( final OptionGroup src, final OptionGroup tgt )
490 {
491 for (Iterator it = src.items(); it.hasNext();)
492 {
493 tgt.addItem((OptionItem)it.next());
494 }
495 }
496
497
501 private static void setAutoCommit( final boolean autoCommit,
502 final Editor editor )
503 {
504 if ( editor instanceof CompoundEditor )
505 {
506 for ( Iterator it = ((CompoundEditor)editor).editors(); it.hasNext(); )
507 {
508 setAutoCommit( autoCommit, (Editor)it.next() );
509 }
510 }
511 if ( editor instanceof ItemEditor )
512 {
513 ((ItemEditor)editor).setAutoCommit( autoCommit );
514 }
515 }
516
517
521 private static void setAutoAdopt( final boolean autoAdopt,
522 final Editor editor )
523 {
524 if ( editor instanceof CompoundEditor )
525 {
526 for ( Iterator it = ((CompoundEditor)editor).editors(); it.hasNext(); )
527 {
528 setAutoAdopt( autoAdopt, (Editor)it.next() );
529 }
530 }
531 if ( editor instanceof ItemEditor )
532 {
533 ((ItemEditor)editor).setAutoAdopt( autoAdopt );
534 }
535 }
536
537
538 private abstract class I18nRenderer
539 implements ListCellRenderer, TableCellRenderer
540 {
541 private final DefaultListCellRenderer listDelegate;
542 private final DefaultTableCellRenderer tableDelegate;
543
544 I18nRenderer()
545 {
546 this.listDelegate = new DefaultListCellRenderer();
547 this.tableDelegate = new DefaultTableCellRenderer();
548 }
549
550 public Component getListCellRendererComponent( final JList list,
551 final Object value,
552 final int index,
553 final boolean isSelected,
554 final boolean hasFocus )
555 {
556 return listDelegate.getListCellRendererComponent(list, toString(value),
557 index,
558 isSelected, hasFocus);
559 }
560
561 public Component getTableCellRendererComponent( final JTable table,
562 final Object value,
563 final boolean isSelected,
564 final boolean hasFocus,
565 final int row,
566 final int column )
567 {
568 return tableDelegate.getTableCellRendererComponent(table, toString(value),
569 isSelected, hasFocus,
570 row, column);
571 }
572
573 abstract String toString( final Object value );
574 }
575
576 private final class ElementsRenderer extends I18nRenderer
577 {
578 String toString( final Object animation )
579 {
580 switch (((Byte)animation).byteValue())
581 {
582 case NO_ANIM:
583 return i18n.getString(DEMO_NAME + ".elements.VALUE.noAnimation");
584 case BLUR_IN:
585 return i18n.getString(DEMO_NAME + ".elements.VALUE.blurIn");
586 case FADE_IN:
587 return i18n.getString(DEMO_NAME + ".elements.VALUE.fadeIn");
588 case IMPLODE:
589 return i18n.getString(DEMO_NAME + ".elements.VALUE.implode");
590 case WHIRL_IN:
591 return i18n.getString(DEMO_NAME + ".elements.VALUE.whirlIn");
592 case EXTRACT:
593 return i18n.getString(DEMO_NAME + ".elements.VALUE.extract");
594 case BLUR_OUT:
595 return i18n.getString(DEMO_NAME + ".elements.VALUE.blurOut");
596 case FADE_OUT:
597 return i18n.getString(DEMO_NAME + ".elements.VALUE.fadeOut");
598 case EXPLODE:
599 return i18n.getString(DEMO_NAME + ".elements.VALUE.explode");
600 case WHIRL_OUT:
601 return i18n.getString(DEMO_NAME + ".elements.VALUE.whirlOut");
602 case RETRACT:
603 return i18n.getString(DEMO_NAME + ".elements.VALUE.retract");
604 default:
605 return "";
607 }
608 }
609 }
610
611 private final class AnimationRenderer extends I18nRenderer
612 {
613 String toString( final Object animation )
614 {
615 switch (((Byte)animation).byteValue())
616 {
617 case NO_ANIM:
618 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.noAnimation");
619 case TRAVERSE_EDGE:
620 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.traverseEdge");
621 case ZOOM:
622 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.zoom");
623 case MOVE_CAMERA:
624 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.moveCamera");
625 case MORPH:
626 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.morph");
627 case RESIZE:
628 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.resize");
629 case BLINK:
630 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.blink");
631 case ANIMATED_LOAD:
632 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.animatedLoad");
633 case ANIMATED_CLEAR:
634 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.animatedClear");
635 default:
636 return i18n.getString(DEMO_NAME + ".misc.animation.VALUE.noAnimation");
637 }
638 }
639 }
640
641 private final class OrderRenderer extends I18nRenderer
642 {
643 String toString( final Object nodeOrder )
644 {
645 if (nodeOrder == ViewAnimationFactory.LEFT_TO_RIGHT)
646 {
647 return i18n.getString("ViewAnimationFactory.LEFT_TO_RIGHT");
648 }
649 if (nodeOrder == ViewAnimationFactory.RIGHT_TO_LEFT)
650 {
651 return i18n.getString("ViewAnimationFactory.RIGHT_TO_LEFT");
652 }
653 if (nodeOrder == ViewAnimationFactory.CLOCKWISE)
654 {
655 return i18n.getString("ViewAnimationFactory.CLOCKWISE");
656 }
657 if (nodeOrder == ViewAnimationFactory.COUNTER_CLOCKWISE)
658 {
659 return i18n.getString("ViewAnimationFactory.COUNTER_CLOCKWISE");
660 }
661 return null;
663 }
664 }
665 }
666