1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.gui.splash;
22
23import proguard.gui.SwingUtil;
24
25import javax.swing.*;
26import java.awt.*;
27import java.awt.event.*;
28import java.lang.reflect.InvocationTargetException;
29
30/**
31 * This JPanel renders an animated Sprite.
32 *
33 * @author Eric Lafortune
34 */
35public class SplashPanel extends JPanel
36{
37    private final MyAnimator  animator  = new MyAnimator();
38    private final MyRepainter repainter = new MyRepainter();
39
40    private final Sprite sprite;
41    private final double sleepFactor;
42
43    private long   startTime = Long.MAX_VALUE;
44    private final long   stopTime;
45
46    private volatile Thread animationThread;
47
48
49    /**
50     * Creates a new SplashPanel with the given Sprite, which will be animated
51     * indefinitely.
52     * @param sprite        the Sprite that will be animated.
53     * @param processorLoad the fraction of processing time to be spend on
54     *                      animating the Sprite (between 0 and 1).
55     */
56    public SplashPanel(Sprite sprite, double processorLoad)
57    {
58        this(sprite, processorLoad, (long)Integer.MAX_VALUE);
59    }
60
61
62    /**
63     * Creates a new SplashPanel with the given Sprite, which will be animated
64     * for a limited period of time.
65     * @param sprite        the Sprite that will be animated.
66     * @param processorLoad the fraction of processing time to be spend on
67     *                      animating the Sprite (between 0 and 1).
68     * @param stopTime      the number of milliseconds after which the
69     *                      animation will be stopped automatically.
70     */
71    public SplashPanel(Sprite sprite, double processorLoad, long stopTime)
72    {
73        this.sprite      = sprite;
74        this.sleepFactor = (1.0-processorLoad) / processorLoad;
75        this.stopTime    = stopTime;
76
77        // Restart the animation on a mouse click.
78        addMouseListener(new MouseAdapter()
79        {
80            public void mouseClicked(MouseEvent e)
81            {
82                SplashPanel.this.start();
83            }
84        });
85    }
86
87
88    /**
89     * Starts the animation.
90     */
91    public void start()
92    {
93        // Go to the beginning of the animation.
94        startTime = System.currentTimeMillis();
95
96        // Make sure we have an animation thread running.
97        if (animationThread == null)
98        {
99            animationThread = new Thread(animator);
100            animationThread.start();
101        }
102    }
103
104
105    /**
106     * Stops the animation.
107     */
108    public void stop()
109    {
110        // Go to the end of the animation.
111        startTime = 0L;
112
113        // Let the animation thread stop itself.
114        animationThread = null;
115
116        // Repaint the SplashPanel one last time.
117        try
118        {
119            SwingUtil.invokeAndWait(repainter);
120        }
121        catch (InterruptedException ex)
122        {
123            // Nothing.
124        }
125        catch (InvocationTargetException ex)
126        {
127            // Nothing.
128        }
129    }
130
131
132    // Implementation for JPanel.
133
134    public void paintComponent(Graphics graphics)
135    {
136        super.paintComponent(graphics);
137
138        sprite.paint(graphics, System.currentTimeMillis() - startTime);
139    }
140
141
142    /**
143     * This Runnable makes sure its SplashPanel gets repainted regularly,
144     * depending on the targeted processor load.
145     */
146    private class MyAnimator implements Runnable
147    {
148        public void run()
149        {
150            try
151            {
152                while (animationThread != null)
153                {
154                    // Check if we should stop the animation.
155                    long time = System.currentTimeMillis();
156                    if (time > startTime + stopTime)
157                    {
158                        animationThread = null;
159                    }
160
161                    // Do a repaint and time it.
162                    SwingUtil.invokeAndWait(repainter);
163
164                    // Sleep for a proportional while.
165                    long repaintTime = System.currentTimeMillis() - time;
166                    long sleepTime   = (long)(sleepFactor * repaintTime);
167                    if (sleepTime < 10L)
168                    {
169                        sleepTime = 10L;
170                    }
171
172                    Thread.sleep(sleepTime);
173                }
174            }
175            catch (InterruptedException ex)
176            {
177                // Nothing.
178            }
179            catch (InvocationTargetException ex)
180            {
181                // Nothing.
182            }
183        }
184    }
185
186
187    /**
188     * This Runnable repaints its SplashPanel.
189     */
190    private class MyRepainter implements Runnable
191    {
192        public void run()
193        {
194            SplashPanel.this.repaint();
195        }
196    }
197
198
199    /**
200     * A main method for testing the splash panel.
201     */
202    public static void main(String[] args)
203    {
204        JFrame frame = new JFrame();
205        frame.setTitle("Animation");
206        frame.setSize(800, 600);
207        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
208        Dimension frameSize  = frame.getSize();
209        frame.setLocation((screenSize.width - frameSize.width)   / 2,
210                          (screenSize.height - frameSize.height) / 2);
211
212        Sprite sprite =
213            new ClipSprite(
214            new ConstantColor(Color.white),
215            new ConstantColor(Color.lightGray),
216            new CircleSprite(true,
217                             new LinearInt(200, 600, new SineTiming(2345L, 0L)),
218                             new LinearInt(200, 400, new SineTiming(3210L, 0L)),
219                             new ConstantInt(150)),
220            new ColorSprite(new ConstantColor(Color.gray),
221            new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
222            new TextSprite(new ConstantString("ProGuard"),
223                           new ConstantInt(200),
224                           new ConstantInt(300)))));
225
226        SplashPanel panel = new SplashPanel(sprite, 0.5);
227        panel.setBackground(Color.white);
228
229        frame.getContentPane().add(panel);
230        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
231        frame.setVisible(true);
232
233        panel.start();
234    }
235}
236