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 }