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