001: package com.xoetrope.swing;
002:
003: import com.xoetrope.swing.animation.AnimationThread;
004: import com.xoetrope.swing.animation.XAnimationSurface;
005: import java.awt.Color;
006: import java.awt.Graphics2D;
007: import java.awt.RenderingHints;
008: import java.awt.geom.AffineTransform;
009: import java.awt.geom.Area;
010: import java.awt.geom.Ellipse2D;
011: import java.awt.geom.Point2D;
012: import java.awt.geom.Rectangle2D;
013:
014: /**
015: *
016: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
017: * the GNU Public License (GPL), please see license.txt for more details. If
018: * you make commercial use of this software you must purchase a commercial
019: * license from Xoetrope.</p>
020: * <p> $Revision: 1.2 $</p>
021: */
022: public class XProgressIndicator extends XAnimationSurface// implements AnimationStep, XAnimationContext
023: {
024: /** Contains the bars composing the circular shape. */
025: protected Area[] ticker = null;
026:
027: /** Notifies whether the animation is running or not. */
028: protected boolean started = false;
029:
030: /** Alpha level of the veil, used for fade in/out. */
031: protected int alphaLevel = 255;
032:
033: /** Duration of the veil's fade in/out. */
034: protected int rampDelay = 1000;
035:
036: /** Alpha level of the veil. */
037: protected float shield = 0.70f;
038:
039: /** Amount of bars composing the circular shape. */
040: protected int barsCount = 14;
041:
042: /** Amount of frames per seconde. Lowers this to save CPU. */
043: protected float fps = 15.0f;
044:
045: /** Rendering hints to set anti aliasing. */
046: protected RenderingHints hints = null;
047: protected double prevWidth, prevHeight;
048:
049: /**
050: * Creates a new progress panel with default values:<br />
051: * <ul>
052: * <li>No message</li>
053: * <li>14 bars</li>
054: * <li>Veil's alpha level is 70%</li>
055: * <li>15 frames per second</li>
056: * <li>Fade in/out last 300 ms</li>
057: * </ul>
058: */
059: public XProgressIndicator() {
060: rampDelay = 300;
061: shield = 0.70f;
062: fps = 15.0f;
063: barsCount = 14;
064:
065: hints = new RenderingHints(RenderingHints.KEY_RENDERING,
066: RenderingHints.VALUE_RENDER_QUALITY);
067: hints.put(RenderingHints.KEY_ANTIALIASING,
068: RenderingHints.VALUE_ANTIALIAS_ON);
069: hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
070: RenderingHints.VALUE_FRACTIONALMETRICS_ON);
071:
072: setDoubleBuffered(true);
073: setLoopTime(60000);
074: setSleepTime(64);
075: }
076:
077: /**
078: * Intializes the progress indicator
079: */
080: public void init() {
081: ticker = buildTicker();
082: prevWidth = getWidth();
083: prevHeight = getHeight();
084: }
085:
086: /**
087: * This method will receive all of the timing events from an Animator
088: * during an animation. The fraction is the percent elapsed (0 to 1)
089: * of the current animation cycle.
090: * @param fraction the fraction of completion between the start and
091: * end of the current cycle. Note that on reversing cycles
092: * ({@link Animator.Direction#BACKWARD}) the fraction decreases
093: * from 1.0 to 0 on backwards-running cycles. Note also that animations
094: * with a duration of {@link Animator#INFINITE INFINITE} will call
095: * timingEvent with an undefined value for fraction, since there is
096: * no fraction that makes sense if the animation has no defined length.
097: * @see Animator.Direction
098: */
099: public void timingEvent(float fraction) {
100: timingFraction = fraction;
101: repaint();
102: }
103:
104: /**
105: * Draw the tick objects to the passed graphics context
106: * @param w <CODE>int</CODE> specifying the width of the component
107: * @param h <CODE>int</CODE> specifying the width of the component
108: * @param g2 <CODE>Graphics2D</CODE>
109: */
110: public void drawObjects(int w, int h, Graphics2D g2) {
111: if ((prevWidth != getWidth()) || (prevHeight != getHeight())) {
112: ticker = buildTicker();
113: prevWidth = getWidth();
114: prevHeight = getHeight();
115: }
116: boolean rampUp = true;
117: Point2D.Double center = new Point2D.Double((double) w / 2.0,
118: (double) h / 2.0);
119: double fixedIncrement = 2.0 * Math.PI / (double) barsCount;
120: AffineTransform toCircle = AffineTransform.getRotateInstance(
121: fixedIncrement, center.getX(), center.getY());
122:
123: for (int i = 0; i < ticker.length; i++)
124: ticker[i].transform(toCircle);
125:
126: drawTicker(g2, w, h);
127: }
128:
129: /**
130: * Draw the indicator to the passed graphics context
131: * @param g2 <CODE>Graphics2D</CODE>
132: * @param width <CODE>int</CODE> specifying the width of the component
133: * @param height <CODE>int</CODE> specifying the width of the component
134: */
135: public void drawTicker(Graphics2D g2, int width, int height) {
136: double maxY = 0.0;
137:
138: g2.setRenderingHints(hints);
139:
140: g2.setColor(new Color(255, 255, 255,
141: (int) (alphaLevel * shield)));
142: g2.fillRect(0, 0, width, height);
143:
144: for (int i = 0; i < ticker.length; i++) {
145: int channel = 224 - 128 / (i + 1);
146: g2
147: .setColor(new Color(channel, channel, channel,
148: alphaLevel));
149: g2.fill(ticker[i]);
150:
151: Rectangle2D bounds = ticker[i].getBounds2D();
152: if (bounds.getMaxY() > maxY)
153: maxY = bounds.getMaxY();
154: }
155: }
156:
157: /**
158: * Builds the circular shape and returns the result as an array of
159: * <code>Area</code>. Each <code>Area</code> is one of the bars
160: * composing the shape.
161: */
162: private Area[] buildTicker() {
163: Area[] ticker = new Area[barsCount];
164: Point2D.Double center = new Point2D.Double(
165: (double) getWidth() / 2, (double) getHeight() / 2);
166: double fixedAngle = 2.0 * Math.PI / ((double) barsCount);
167:
168: for (double i = 0.0; i < (double) barsCount; i++) {
169: Area primitive = buildPrimitive();
170:
171: AffineTransform toCenter = AffineTransform
172: .getTranslateInstance(center.getX(), center.getY());
173: AffineTransform toBorder = AffineTransform
174: .getTranslateInstance(45.0, -6.0);
175: AffineTransform toCircle = AffineTransform
176: .getRotateInstance(-i * fixedAngle, center.getX(),
177: center.getY());
178:
179: AffineTransform toWheel = new AffineTransform();
180: toWheel.concatenate(toCenter);
181: toWheel.concatenate(toBorder);
182:
183: primitive.transform(toWheel);
184: primitive.transform(toCircle);
185:
186: ticker[(int) i] = primitive;
187: }
188:
189: return ticker;
190: }
191:
192: /**
193: * Builds a bar.
194: */
195: private Area buildPrimitive() {
196: Rectangle2D.Double body = new Rectangle2D.Double(6.0, 0.0,
197: 30.0, 12.0);
198: Ellipse2D.Double head = new Ellipse2D.Double(0.0, 0.0, 12.0,
199: 12.0);
200: Ellipse2D.Double tail = new Ellipse2D.Double(30.0, 0.0, 12.0,
201: 12.0);
202:
203: Area tick = new Area(body);
204: tick.add(new Area(head));
205: tick.add(new Area(tail));
206:
207: return tick;
208: }
209: }
|