001package myproject.util;
002
003import java.awt.Color;
004import java.awt.Dimension;
005import java.awt.Graphics;
006import java.util.Observable;
007import javax.swing.JFrame;
008import javax.swing.JPanel;
009import javax.swing.SwingUtilities;
010import javax.swing.WindowConstants;
011
012/**
013 * A swing implementation of {@link Animator}, using a {@link JFrame}
014 * to display the animation.  The {@link JFrame} is created and
015 * displayed by the constructor.
016 *
017 * Calls to <code>update()</code> result in a call to
018 * <code>painter.paint()</code>.  This is executed in the swing
019 * thread while the main thread is paused for <code>delay</code>
020 * milliseconds.
021 */
022public class SwingAnimator implements Animator {
023        // The following fields are manipulated by the main program thread
024        private int delay;
025
026        // The following fields are manipulated by the swing thread
027        private JFrame frame; // Swing representation of an OS window
028        private ContentPane content; // A paintable component
029        private boolean disposed = false; // If true, then die
030
031        /**
032         * Creates and displays a {@link JFrame} for the animation.
033         * @param name  The name to be displayed on the graphical window.
034         * @param width The width of the display, in pixels.
035         * @param height The height of the display, in pixels.
036         * @param delay Time to pause after an update, in milliseconds.
037         */
038        public SwingAnimator(final SwingAnimatorPainter painter, final String name, final int width, final int height, int delay) {
039                this.delay = delay;
040                // Create a graphics window and display it
041                SwingUtilities.invokeLater(() -> {
042                        content = new ContentPane(painter, width, height); // A paintable component for content
043                        frame = new JFrame();  // An OS window
044                        frame.setTitle(name);  // The title of the Frame
045                        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);  // End program if Frame is closed
046                        frame.setContentPane(content); // Associate the content with the Frame
047                        frame.pack(); // Fix the layout of the Frame
048                        frame.setVisible(true); // Display the Frame
049                });
050        }
051
052        /**
053         * Throw away this visualization.
054         */
055        public void dispose() {
056                SwingUtilities.invokeLater(() -> {
057                        frame.dispose();
058                        disposed = true;
059                });
060        }
061
062        /**
063         * Calls to <code>update</code> are executed in the swing thread,
064         * while the main thread is paused for <code>delay</code>
065         * milliseconds.
066         */
067        public void update(final Observable model, Object ignored) {
068                if (disposed)
069                        throw new IllegalStateException();
070
071                // Redraw the window
072                //   content.repaint() causes a call to content.paint(g)
073                //   where g is an appropriate graphics argument.
074                SwingUtilities.invokeLater(() -> content.repaint());
075
076                // Delay the main thread
077                try {
078                        Thread.sleep(delay);
079                } catch (InterruptedException e) {}
080        }
081
082        /**
083         * A component for painting.
084         * All code is executed in the swing thread.
085         */
086        private static class ContentPane extends JPanel {
087                private static final long serialVersionUID = 2008L;
088                private int width;
089                private int height;
090                private SwingAnimatorPainter painter;
091
092                ContentPane(SwingAnimatorPainter painter, int width, int height) {
093                        this.painter = painter;
094                        this.width = width;
095                        this.height = height;
096                        setPreferredSize(new Dimension(width, height));
097                        setDoubleBuffered(true);
098                        setOpaque(true);
099                        setBackground(Color.WHITE);
100                }
101
102                public void paint(Graphics g) {
103                        // This test is necessary because the swing thread may call this
104                        // method before the simulation calls SwingAnimator.update()
105                        if (painter != null ) {
106                                // The clearRect is necessary, since JPanel is lightweight
107                                g.clearRect(0, 0, width, height);
108                                painter.paint(g);
109                        }
110                }
111        }
112}