001    /*
002     * Copyright 2002-2006 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.springframework.richclient.application.splash;
017    
018    import java.awt.Color;
019    import java.awt.Graphics;
020    import java.awt.Graphics2D;
021    import java.awt.Image;
022    import java.awt.Point;
023    import java.awt.RenderingHints;
024    import java.awt.event.MouseEvent;
025    import java.awt.event.MouseListener;
026    import java.awt.font.FontRenderContext;
027    import java.awt.font.TextLayout;
028    import java.awt.geom.AffineTransform;
029    import java.awt.geom.Area;
030    import java.awt.geom.Ellipse2D;
031    import java.awt.geom.Point2D;
032    import java.awt.geom.Rectangle2D;
033    
034    import javax.swing.JComponent;
035    
036    /**
037     * Taken from a blog post by <a
038     * href="http://jroller.com/page/gfx?entry=wait_with_style_in_swing">Romain Guy</a>.
039     * <p>
040     * The only change is support for a background image.
041     * 
042     * @author Romain Guy
043     */
044    public class InfiniteProgressPanel extends JComponent implements MouseListener {
045            protected Area[] ticker = null;
046    
047            protected Thread animation = null;
048    
049            protected boolean started = false;
050    
051            protected int alphaLevel = 0;
052    
053            protected int rampDelay = 300;
054    
055            protected float shield = 0.70f;
056    
057            protected String text = "";
058    
059            protected int barsCount = 14;
060    
061            protected float fps = 15.0f;
062    
063            private Image background;
064    
065            protected RenderingHints hints = null;
066    
067            public InfiniteProgressPanel() {
068                    this("");
069            }
070    
071            public InfiniteProgressPanel(String text) {
072                    this(text, 14);
073            }
074    
075            public InfiniteProgressPanel(String text, int barsCount) {
076                    this(text, barsCount, 0.70f);
077            }
078    
079            public InfiniteProgressPanel(String text, int barsCount, float shield) {
080                    this(text, barsCount, shield, 15.0f);
081            }
082    
083            public InfiniteProgressPanel(String text, int barsCount, float shield, float fps) {
084                    this(text, barsCount, shield, fps, 300);
085            }
086    
087            public InfiniteProgressPanel(String text, int barsCount, float shield, float fps, int rampDelay) {
088                    this.text = text;
089                    this.rampDelay = rampDelay >= 0 ? rampDelay : 0;
090                    this.shield = shield >= 0.0f ? shield : 0.0f;
091                    this.fps = fps > 0.0f ? fps : 15.0f;
092                    this.barsCount = barsCount > 0 ? barsCount : 14;
093    
094                    this.hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
095                    this.hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
096                    this.hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
097            }
098    
099            public void setText(String text) {
100                    repaint();
101                    this.text = text;
102            }
103    
104            public String getText() {
105                    return text;
106            }
107    
108            public void start() {
109                    addMouseListener(this);
110                    setVisible(true);
111                    ticker = buildTicker();
112                    animation = new Thread(new Animator(true));
113                    animation.start();
114            }
115    
116            public void stop() {
117                    if (animation != null) {
118                            animation.interrupt();
119                            animation = null;
120                            animation = new Thread(new Animator(false));
121                            animation.start();
122                    }
123            }
124    
125            public void interrupt() {
126                    if (animation != null) {
127                            animation.interrupt();
128                            animation = null;
129    
130                            removeMouseListener(this);
131                            setVisible(false);
132                    }
133            }
134    
135            public void paintComponent(Graphics g) {
136                    if (started) {
137                            int width = getWidth();
138                            int height = getHeight();
139    
140                            double maxY = 0.0;
141    
142                            Graphics2D g2 = (Graphics2D) g;
143                            g2.setRenderingHints(hints);
144    
145                            if (background != null) {
146                                    Point location = getLocationOnScreen();
147                                    g2.drawImage(background, -location.x, -location.y, null);
148                            }
149    
150                            g2.setColor(new Color(255, 255, 255, (int) (alphaLevel * shield)));
151                            g2.fillRect(0, 0, getWidth(), getHeight());
152    
153                            for (int i = 0; i < ticker.length; i++) {
154                                    int channel = 224 - 128 / (i + 1);
155                                    g2.setColor(new Color(channel, channel, channel, alphaLevel));
156                                    g2.fill(ticker[i]);
157    
158                                    Rectangle2D bounds = ticker[i].getBounds2D();
159                                    if (bounds.getMaxY() > maxY)
160                                            maxY = bounds.getMaxY();
161                            }
162    
163                            if (text != null && text.length() > 0) {
164                                    FontRenderContext context = g2.getFontRenderContext();
165                                    TextLayout layout = new TextLayout(text, getFont(), context);
166                                    Rectangle2D bounds = layout.getBounds();
167                                    g2.setColor(getForeground());
168                                    layout.draw(g2, (float) (width - bounds.getWidth()) / 2,
169                                                    (float) (maxY + layout.getLeading() + 2 * layout.getAscent()));
170                            }
171                    }
172            }
173    
174            private Area[] buildTicker() {
175                    Area[] ticker = new Area[barsCount];
176                    Point2D.Double center = new Point2D.Double((double) getWidth() / 2, (double) getHeight() / 2);
177                    double fixedAngle = 2.0 * Math.PI / ((double) barsCount);
178    
179                    for (double i = 0.0; i < (double) barsCount; i++) {
180                            Area primitive = buildPrimitive();
181    
182                            AffineTransform toCenter = AffineTransform.getTranslateInstance(center.getX(), center.getY());
183                            AffineTransform toBorder = AffineTransform.getTranslateInstance(45.0, -6.0);
184                            AffineTransform toCircle = AffineTransform.getRotateInstance(-i * fixedAngle, center.getX(), center.getY());
185    
186                            AffineTransform toWheel = new AffineTransform();
187                            toWheel.concatenate(toCenter);
188                            toWheel.concatenate(toBorder);
189    
190                            primitive.transform(toWheel);
191                            primitive.transform(toCircle);
192    
193                            ticker[(int) i] = primitive;
194                    }
195    
196                    return ticker;
197            }
198    
199            private Area buildPrimitive() {
200                    Rectangle2D.Double body = new Rectangle2D.Double(6, 0, 30, 12);
201                    Ellipse2D.Double head = new Ellipse2D.Double(0, 0, 12, 12);
202                    Ellipse2D.Double tail = new Ellipse2D.Double(30, 0, 12, 12);
203    
204                    Area tick = new Area(body);
205                    tick.add(new Area(head));
206                    tick.add(new Area(tail));
207    
208                    return tick;
209            }
210    
211            protected class Animator implements Runnable {
212                    private boolean rampUp = true;
213    
214                    protected Animator(boolean rampUp) {
215                            this.rampUp = rampUp;
216                    }
217    
218                    public void run() {
219                            Point2D.Double center = new Point2D.Double((double) getWidth() / 2, (double) getHeight() / 2);
220                            double fixedIncrement = 2.0 * Math.PI / ((double) barsCount);
221                            AffineTransform toCircle = AffineTransform.getRotateInstance(fixedIncrement, center.getX(), center.getY());
222    
223                            long start = System.currentTimeMillis();
224                            if (rampDelay == 0)
225                                    alphaLevel = rampUp ? 255 : 0;
226    
227                            started = true;
228                            boolean inRamp = rampUp;
229    
230                            while (!Thread.interrupted()) {
231                                    if (!inRamp) {
232                                            for (int i = 0; i < ticker.length; i++)
233                                                    ticker[i].transform(toCircle);
234                                    }
235    
236                                    repaint();
237    
238                                    if (rampUp) {
239                                            if (alphaLevel < 255) {
240                                                    alphaLevel = (int) (255 * (System.currentTimeMillis() - start) / rampDelay);
241                                                    if (alphaLevel >= 255) {
242                                                            alphaLevel = 255;
243                                                            inRamp = false;
244                                                    }
245                                            }
246                                    }
247                                    else if (alphaLevel > 0) {
248                                            alphaLevel = (int) (255 - (255 * (System.currentTimeMillis() - start) / rampDelay));
249                                            if (alphaLevel <= 0) {
250                                                    alphaLevel = 0;
251                                                    break;
252                                            }
253                                    }
254    
255                                    try {
256                                            Thread.sleep(inRamp ? 10 : (int) (1000 / fps));
257                                    }
258                                    catch (InterruptedException ie) {
259                                            break;
260                                    }
261                                    Thread.yield();
262                            }
263    
264                            if (!rampUp) {
265                                    started = false;
266                                    repaint();
267    
268                                    setVisible(false);
269                                    removeMouseListener(InfiniteProgressPanel.this);
270                            }
271                    }
272            }
273    
274            public void mouseClicked(MouseEvent e) {
275            }
276    
277            public void mousePressed(MouseEvent e) {
278            }
279    
280            public void mouseReleased(MouseEvent e) {
281            }
282    
283            public void mouseEntered(MouseEvent e) {
284            }
285    
286            public void mouseExited(MouseEvent e) {
287            }
288    
289            public void setBackground(Image background) {
290                    this.background = background;
291            }
292    }