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