001: package abbot.editor.widgets;
002:
003: import java.awt.AlphaComposite;
004: import java.awt.BasicStroke;
005: import java.awt.Color;
006: import java.awt.Component;
007: import java.awt.Graphics;
008: import java.awt.Graphics2D;
009: import java.awt.Image;
010: import java.awt.RenderingHints;
011: import java.awt.Transparency;
012: import java.awt.image.BufferedImage;
013: import javax.swing.Icon;
014: import javax.swing.ImageIcon;
015:
016: /** Provides a spinning disk of hash marks. */
017: public class SpinningDial extends AbstractAnimatedIcon {
018: private static final int MIN_ALPHA = 32;
019: private static final int DEFAULT_SIZE = 32;
020: private static final int SPOKES = 16;
021: /** This is a good delay between increasing the phase. */
022: public static final int SPIN_INTERVAL = 1000 / SPOKES;
023: private int w;
024: private int h;
025: private Image[] frames;
026:
027: public SpinningDial() {
028: this (DEFAULT_SIZE, DEFAULT_SIZE);
029: }
030:
031: /** Not recommended to go below 16 pixels. */
032: public SpinningDial(int diameter) {
033: this (diameter, diameter);
034: }
035:
036: /** Not recommended to go below 16x16. */
037: public SpinningDial(int w, int h) {
038: this (w, h, SPOKES);
039: }
040:
041: public SpinningDial(int w, int h, int spokes) {
042: super (spokes, SPIN_INTERVAL);
043: this .w = w;
044: this .h = h;
045: frames = new Image[getFrameCount()];
046: }
047:
048: public int getIconHeight() {
049: return h;
050: }
051:
052: public int getIconWidth() {
053: return w;
054: }
055:
056: /** Set the stroke width according to the size. */
057: protected float getStrokeWidth(int size) {
058: return size / 16f;
059: }
060:
061: // TODO: move image snapshot up to abstract class
062: protected void paintFrame(Component c, Graphics graphics, int x,
063: int y) {
064: int idx = getFrame();
065: if (frames[idx] == null) {
066: int w = getIconWidth();
067: int h = getIconHeight();
068: int size = Math.min(w, h);
069: Image image = c != null ? c.getGraphicsConfiguration()
070: .createCompatibleImage(w, h,
071: Transparency.TRANSLUCENT)
072: : new BufferedImage(w, h,
073: BufferedImage.TYPE_INT_ARGB);
074: Graphics2D g = (Graphics2D) image.getGraphics();
075: g.setComposite(AlphaComposite.Clear);
076: g.fillRect(0, 0, w, h);
077: g.setComposite(AlphaComposite.Src);
078: g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
079: RenderingHints.VALUE_ANTIALIAS_ON);
080: final int FULL_SIZE = 256;
081: float strokeWidth = getStrokeWidth(FULL_SIZE);
082: float fraction = .6f;
083: g.setStroke(new BasicStroke(strokeWidth,
084: BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
085: g.translate((float) w / 2, (float) h / 2);
086: float scale = (float) size / FULL_SIZE;
087: g.scale(scale, scale);
088: int alpha = 255;
089: int x1, y1, x2, y2;
090: int radius = FULL_SIZE / 2 - 1 - (int) (strokeWidth / 2);
091: int frameCount = getFrameCount();
092: for (int i = 0; i < frameCount; i++) {
093: double cos = Math.cos(Math.PI * 2 - Math.PI * 2
094: * (i - idx) / frameCount);
095: double sin = Math.sin(Math.PI * 2 - Math.PI * 2
096: * (i - idx) / frameCount);
097: x1 = (int) (radius * fraction * cos);
098: x2 = (int) (radius * cos);
099: y1 = (int) (radius * fraction * sin);
100: y2 = (int) (radius * sin);
101: g.setColor(new Color(0, 0, 0, Math.min(255, alpha)));
102: g.drawLine(x1, y1, x2, y2);
103: alpha = Math.max(MIN_ALPHA, alpha * 3 / 4);
104: }
105: g.dispose();
106: frames[idx] = image;
107: }
108: graphics.drawImage(frames[idx], x, y, null);
109: }
110:
111: public String toString() {
112: return "SpinningDial(" + getIconWidth() + "x" + getIconHeight()
113: + ")";
114: }
115: }
|