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 }