KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > chatserver > client > InfiniteProgressPanel


1 /*
2  * Copyright (c) 2005, Romain Guy <romain.guy@jext.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8  * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9  *
10  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11  *
12  * Code from Romain Guy's webblog - http://www.jroller.com/page/gfx
13  * Subject to the BSD license.
14  *
15  * For questions, suggestions, bug-reports, enhancement-requests etc.
16  * visit http://www.quickserver.org
17  */

18 package chatserver.client;
19
20 import java.awt.Color JavaDoc;
21 import java.awt.Graphics JavaDoc;
22 import java.awt.Graphics2D JavaDoc;
23 import java.awt.RenderingHints JavaDoc;
24 import java.awt.event.MouseEvent JavaDoc;
25 import java.awt.event.MouseListener JavaDoc;
26 import java.awt.font.FontRenderContext JavaDoc;
27 import java.awt.font.TextLayout JavaDoc;
28 import java.awt.geom.AffineTransform JavaDoc;
29 import java.awt.geom.Area JavaDoc;
30 import java.awt.geom.Ellipse2D JavaDoc;
31 import java.awt.geom.Point2D JavaDoc;
32 import java.awt.geom.Rectangle2D JavaDoc;
33
34 import javax.swing.JComponent JavaDoc;
35
36 /**
37  *
38  * An infinite progress panel displays a rotating figure and
39  * a message to notice the user of a long, duration unknown
40  * task. The shape and the text are drawn upon a white veil
41  * which alpha level (or shield value) lets the underlying
42  * component shine through. This panel is meant to be used
43  * asa <i>glass pane</i> in the window performing the long
44  * operation.
45  * <br /><br />
46  * On the contrary to regular glass panes, you don't need to
47  * set it visible or not by yourself. Once you've started the
48  * animation all the mouse events are intercepted by this
49  * panel, preventing them from being forwared to the
50  * underlying components.
51  * <br /><br />
52  * The panel can be controlled by the <code>start()</code>,
53  * <code>stop()</code> and <code>interrupt()</code> methods.
54  * <br /><br />
55  * Example:
56  * <br /><br />
57  * <pre>InfiniteProgressPanel pane = new InfiniteProgressPanel();
58  * frame.setGlassPane(pane);
59  * pane.start()</pre>
60  * <br /><br />
61  * Several properties can be configured at creation time. The
62  * message and its font can be changed at runtime. Changing the
63  * font can be done using <code>setFont()</code> and
64  * <code>setForeground()</code>.
65  *
66  * Code from Romain Guy's webblog - http://www.jroller.com/page/gfx (BSD license.)
67  *
68  * @author Romain Guy
69  * @version 1.0
70  */

71 public class InfiniteProgressPanel extends JComponent JavaDoc implements MouseListener JavaDoc {
72     /** Contains the bars composing the circular shape. */
73     protected Area JavaDoc[] ticker = null;
74     /** The animation thread is responsible for fade in/out and rotation. */
75     protected Thread JavaDoc animation = null;
76     /** Notifies whether the animation is running or not. */
77     protected boolean started = false;
78     /** Alpha level of the veil, used for fade in/out. */
79     protected int alphaLevel = 0;
80     /** Duration of the veil's fade in/out. */
81     protected int rampDelay = 300;
82     /** Alpha level of the veil. */
83     protected float shield = 0.70f;
84     /** Message displayed below the circular shape. */
85     protected String JavaDoc text = "";
86     /** Amount of bars composing the circular shape. */
87     protected int barsCount = 14;
88     /** Amount of frames per seconde. Lowers this to save CPU. */
89     protected float fps = 15.0f;
90     /** Rendering hints to set anti aliasing. */
91     protected RenderingHints JavaDoc hints = null;
92     
93     /**
94      * Creates a new progress panel with default values:<br />
95      * <ul>
96      * <li>No message</li>
97      * <li>14 bars</li>
98      * <li>Veil's alpha level is 70%</li>
99      * <li>15 frames per second</li>
100      * <li>Fade in/out last 300 ms</li>
101      * </ul>
102      */

103     public InfiniteProgressPanel() {
104         this("");
105     }
106     
107     /**
108      * Creates a new progress panel with default values:<br />
109      * <ul>
110      * <li>14 bars</li>
111      * <li>Veil's alpha level is 70%</li>
112      * <li>15 frames per second</li>
113      * <li>Fade in/out last 300 ms</li>
114      * </ul>
115      * @param text The message to be displayed. Can be null or empty.
116      */

117     public InfiniteProgressPanel(String JavaDoc text) {
118         this(text, 14);
119     }
120     
121     /**
122      * Creates a new progress panel with default values:<br />
123      * <ul>
124      * <li>Veil's alpha level is 70%</li>
125      * <li>15 frames per second</li>
126      * <li>Fade in/out last 300 ms</li>
127      * </ul>
128      * @param text The message to be displayed. Can be null or empty.
129      * @param barsCount The amount of bars composing the circular shape
130      */

131     public InfiniteProgressPanel(String JavaDoc text, int barsCount) {
132         this(text, barsCount, 0.70f);
133     }
134     
135     /**
136      * Creates a new progress panel with default values:<br />
137      * <ul>
138      * <li>15 frames per second</li>
139      * <li>Fade in/out last 300 ms</li>
140      * </ul>
141      * @param text The message to be displayed. Can be null or empty.
142      * @param barsCount The amount of bars composing the circular shape.
143      * @param shield The alpha level between 0.0 and 1.0 of the colored
144      * shield (or veil).
145      */

146     public InfiniteProgressPanel(String JavaDoc text, int barsCount, float shield) {
147         this(text, barsCount, shield, 15.0f);
148     }
149     
150     /**
151      * Creates a new progress panel with default values:<br />
152      * <ul>
153      * <li>Fade in/out last 300 ms</li>
154      * </ul>
155      * @param text The message to be displayed. Can be null or empty.
156      * @param barsCount The amount of bars composing the circular shape.
157      * @param shield The alpha level between 0.0 and 1.0 of the colored
158      * shield (or veil).
159      * @param fps The number of frames per second. Lower this value to
160      * decrease CPU usage.
161      */

162     public InfiniteProgressPanel(String JavaDoc text, int barsCount, float shield, float fps) {
163         this(text, barsCount, shield, fps, 300);
164     }
165     
166     /**
167      * Creates a new progress panel.
168      * @param text The message to be displayed. Can be null or empty.
169      * @param barsCount The amount of bars composing the circular shape.
170      * @param shield The alpha level between 0.0 and 1.0 of the colored
171      * shield (or veil).
172      * @param fps The number of frames per second. Lower this value to
173      * decrease CPU usage.
174      * @param rampDelay The duration, in milli seconds, of the fade in and
175      * the fade out of the veil.
176      */

177     public InfiniteProgressPanel(String JavaDoc text, int barsCount, float shield, float fps, int rampDelay) {
178         this.text = text;
179         this.rampDelay = rampDelay >= 0 ? rampDelay : 0;
180         this.shield = shield >= 0.0f ? shield : 0.0f;
181         this.fps = fps > 0.0f ? fps : 15.0f;
182         this.barsCount = barsCount > 0 ? barsCount : 14;
183         
184         this.hints = new RenderingHints JavaDoc(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
185         this.hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
186         this.hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
187     }
188     
189     /**
190      * Changes the displayed message at runtime.
191      *
192      * @param text The message to be displayed. Can be null or empty.
193      */

194     public void setText(String JavaDoc text) {
195         this.text = text;
196         repaint();
197     }
198     
199     /**
200      * Returns the current displayed message.
201      */

202     public String JavaDoc getText() {
203         return text;
204     }
205     
206     /**
207      * Starts the waiting animation by fading the veil in, then
208      * rotating the shapes. This method handles the visibility
209      * of the glass pane.
210      */

211     public void start() {
212         addMouseListener(this);
213         setVisible(true);
214         ticker = buildTicker();
215         animation = new Thread JavaDoc(new Animator(true));
216         animation.start();
217     }
218     
219     /**
220      * Stops the waiting animation by stopping the rotation
221      * of the circular shape and then by fading out the veil.
222      * This methods sets the panel invisible at the end.
223      */

224     public void stop() {
225         if (animation != null) {
226             animation.interrupt();
227             animation = null;
228             animation = new Thread JavaDoc(new Animator(false));
229             animation.start();
230         }
231     }
232     
233     /**
234      * Interrupts the animation, whatever its state is. You
235      * can use it when you need to stop the animation without
236      * running the fade out phase.
237      * This methods sets the panel invisible at the end.
238      */

239     public void interrupt() {
240         if (animation != null) {
241             animation.interrupt();
242             animation = null;
243             
244             removeMouseListener(this);
245             setVisible(false);
246         }
247     }
248     
249     public void paintComponent(Graphics JavaDoc g) {
250         if (started) {
251             int width = getWidth();
252             int height = getHeight();
253             
254             double maxY = 0.0;
255             
256             Graphics2D JavaDoc g2 = (Graphics2D JavaDoc) g;
257             g2.setRenderingHints(hints);
258             
259             g2.setColor(new Color JavaDoc(255, 255, 255, (int) (alphaLevel * shield)));
260             g2.fillRect(0, 0, getWidth(), getHeight());
261             
262             for (int i = 0; i < ticker.length; i++) {
263                 int channel = 224 - 128 / (i + 1);
264                 g2.setColor(new Color JavaDoc(channel, channel, channel, alphaLevel));
265                 g2.fill(ticker[i]);
266                 
267                 Rectangle2D JavaDoc bounds = ticker[i].getBounds2D();
268                 if (bounds.getMaxY() > maxY)
269                     maxY = bounds.getMaxY();
270             }
271             
272             if (text != null && text.length() > 0) {
273                 FontRenderContext JavaDoc context = g2.getFontRenderContext();
274                 TextLayout JavaDoc layout = new TextLayout JavaDoc(text, getFont(), context);
275                 Rectangle2D JavaDoc bounds = layout.getBounds();
276                 g2.setColor(getForeground());
277                 layout.draw(g2, (float) (width - bounds.getWidth()) / 2,
278                         (float) (maxY + layout.getLeading() + 2 * layout.getAscent()));
279             }
280         }
281     }
282     
283     /**
284      * Builds the circular shape and returns the result as an array of
285      * <code>Area</code>. Each <code>Area</code> is one of the bars
286      * composing the shape.
287      */

288     private Area JavaDoc[] buildTicker() {
289         Area JavaDoc[] ticker = new Area JavaDoc[barsCount];
290         Point2D.Double JavaDoc center = new Point2D.Double JavaDoc((double) getWidth() / 2, (double) getHeight() / 2);
291         double fixedAngle = 2.0 * Math.PI / ((double) barsCount);
292         
293         for (double i = 0.0; i < (double) barsCount; i++) {
294             Area JavaDoc primitive = buildPrimitive();
295             
296             AffineTransform JavaDoc toCenter = AffineTransform.getTranslateInstance(center.getX(), center.getY());
297             AffineTransform JavaDoc toBorder = AffineTransform.getTranslateInstance(45.0, -6.0);
298             AffineTransform JavaDoc toCircle = AffineTransform.getRotateInstance(-i * fixedAngle, center.getX(), center.getY());
299             
300             AffineTransform JavaDoc toWheel = new AffineTransform JavaDoc();
301             toWheel.concatenate(toCenter);
302             toWheel.concatenate(toBorder);
303             
304             primitive.transform(toWheel);
305             primitive.transform(toCircle);
306             
307             ticker[(int) i] = primitive;
308         }
309         
310         return ticker;
311     }
312     
313     /**
314      * Builds a bar.
315      */

316     private Area JavaDoc buildPrimitive() {
317         Rectangle2D.Double JavaDoc body = new Rectangle2D.Double JavaDoc(6, 0, 30, 12);
318         Ellipse2D.Double JavaDoc head = new Ellipse2D.Double JavaDoc(0, 0, 12, 12);
319         Ellipse2D.Double JavaDoc tail = new Ellipse2D.Double JavaDoc(30, 0, 12, 12);
320         
321         Area JavaDoc tick = new Area JavaDoc(body);
322         tick.add(new Area JavaDoc(head));
323         tick.add(new Area JavaDoc(tail));
324         
325         return tick;
326     }
327     
328     /**
329      * Animation thread.
330      */

331     private class Animator implements Runnable JavaDoc {
332         private boolean rampUp = true;
333         
334         protected Animator(boolean rampUp) {
335             this.rampUp = rampUp;
336         }
337         
338         public void run() {
339             Point2D.Double JavaDoc center = new Point2D.Double JavaDoc((double) getWidth() / 2, (double) getHeight() / 2);
340             double fixedIncrement = 2.0 * Math.PI / ((double) barsCount);
341             AffineTransform JavaDoc toCircle = AffineTransform.getRotateInstance(fixedIncrement, center.getX(), center.getY());
342             
343             long start = System.currentTimeMillis();
344             if (rampDelay == 0)
345                 alphaLevel = rampUp ? 255 : 0;
346             
347             started = true;
348             boolean inRamp = rampUp;
349             
350             while (!Thread.interrupted()) {
351                 if (!inRamp) {
352                     for (int i = 0; i < ticker.length; i++)
353                         ticker[i].transform(toCircle);
354                 }
355                 
356                 repaint();
357                 
358                 if (rampUp) {
359                     if (alphaLevel < 255) {
360                         alphaLevel = (int) (255 * (System.currentTimeMillis() - start) / rampDelay);
361                         if (alphaLevel >= 255) {
362                             alphaLevel = 255;
363                             inRamp = false;
364                         }
365                     }
366                 } else if (alphaLevel > 0) {
367                     alphaLevel = (int) (255 - (255 * (System.currentTimeMillis() - start) / rampDelay));
368                     if (alphaLevel <= 0) {
369                         alphaLevel = 0;
370                         break;
371                     }
372                 }
373                 
374                 try {
375                     Thread.sleep(inRamp ? 10 : (int) (1000 / fps));
376                 } catch (InterruptedException JavaDoc ie) {
377                     break;
378                 }
379                 Thread.yield();
380             }
381             
382             if (!rampUp) {
383                 started = false;
384                 repaint();
385                 
386                 setVisible(false);
387                 removeMouseListener(InfiniteProgressPanel.this);
388             }
389         }
390     }
391     
392     public void mouseClicked(MouseEvent JavaDoc e) {
393     }
394     
395     public void mousePressed(MouseEvent JavaDoc e) {
396     }
397     
398     public void mouseReleased(MouseEvent JavaDoc e) {
399     }
400     
401     public void mouseEntered(MouseEvent JavaDoc e) {
402     }
403     
404     public void mouseExited(MouseEvent JavaDoc e) {
405     }
406 }
407
Popular Tags