001: package abbot.editor.widgets;
002:
003: import java.awt.Color;
004: import java.awt.Font;
005: import java.awt.FontMetrics;
006: import java.awt.Graphics;
007: import java.awt.Graphics2D;
008: import java.awt.Rectangle;
009: import java.awt.RenderingHints;
010: import java.awt.event.ActionEvent;
011: import java.awt.event.ActionListener;
012: import java.awt.geom.Rectangle2D;
013: import javax.swing.JComponent;
014: import javax.swing.JFrame;
015: import javax.swing.JLabel;
016: import javax.swing.JMenuBar;
017: import javax.swing.Timer;
018: import abbot.util.AWT;
019:
020: /* Gradually fade the display, then show a spinning dial indicator.
021: */
022: // TODO: provide text next to spinner (i.e. paint JLabel instead of icon)
023: public class SpinningDialWaitIndicator extends WaitIndicator implements
024: ActionListener {
025:
026: private static final int MAX_SIZE = 64;
027: private static final int FADE_INTERVAL = 1000 / 24;
028: private static final int FADE_THRESHOLD = 192;
029: // size of margins when space is sufficiently large
030: private static final int MARGIN = 8;
031: // margins take up 1/MARGIN_FRACTION when space is limited
032: private static final int MARGIN_FRACTION = 8;
033:
034: private Timer timer;
035: private int fade;
036: private int verticalOffset;
037: private SpinningDial dial;
038: private String text;
039:
040: public SpinningDialWaitIndicator(JFrame frame) {
041: this (frame.getLayeredPane());
042: JMenuBar mb = frame.getJMenuBar();
043: if (mb != null) {
044: // Exclude the menu bar from centering/sizing
045: verticalOffset = mb.getHeight();
046: }
047: }
048:
049: public SpinningDialWaitIndicator(JComponent target) {
050: this (target, null);
051: }
052:
053: public SpinningDialWaitIndicator(final JComponent target,
054: String text) {
055: super (target);
056: this .text = text;
057: // Draw the dial centered and scaled to fit, up to a maximum size
058: dial = new SpinningDial() {
059: public int getIconWidth() {
060: Rectangle r = getComponent().getVisibleRect();
061: int margin = Math
062: .min(MARGIN, r.width / MARGIN_FRACTION);
063: return Math.min(MAX_SIZE, r.width - margin * 2);
064: }
065:
066: public int getIconHeight() {
067: Rectangle r = getComponent().getVisibleRect();
068: int margin = Math.min(MARGIN, r.height
069: / MARGIN_FRACTION);
070: return Math.min(MAX_SIZE, r.height - verticalOffset
071: - margin * 2);
072: }
073: };
074: // Disable automatic animation
075: dial.setFrameInterval(0);
076: }
077:
078: public void setText(String text) {
079: this .text = text;
080: repaint();
081: }
082:
083: /** Fade the affected component to background, then apply a spinning
084: * wait indicator.
085: */
086: public void paint(Graphics graphics) {
087: if (timer == null) {
088: timer = new Timer(FADE_INTERVAL, this );
089: timer.start();
090: }
091: Graphics2D g = (Graphics2D) graphics.create();
092:
093: Rectangle r = getComponent().getVisibleRect();
094: g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
095: RenderingHints.VALUE_ANTIALIAS_ON);
096: g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
097: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
098: g.setColor(AWT.alpha(getComponent().getBackground(), fade));
099: g.fillRect(r.x, r.y, r.width, r.height);
100: if (fade < FADE_THRESHOLD)
101: return;
102:
103: int x = r.x;
104: if (text == null)
105: x += (r.width - dial.getIconWidth()) / 2;
106: else
107: x += dial.getIconWidth() / 4;
108: int y = r.y + verticalOffset
109: + (r.height - verticalOffset - dial.getIconHeight())
110: / 2;
111: dial.paintIcon(getPainter(), g, x, y);
112: if (text != null) {
113: Font font = g.getFont();
114: g.setFont(font.deriveFont(Font.BOLD,
115: dial.getIconHeight() / 2));
116: FontMetrics m = g.getFontMetrics();
117: x += dial.getIconWidth() * 5 / 4;
118: // FIXME vertical offset is not quite right
119: y += dial.getIconHeight()
120: - (dial.getIconHeight() - m.getAscent()) / 2;
121: g.setColor(getComponent().getForeground());
122: g.drawString(text, x, y);
123: }
124:
125: g.dispose();
126: }
127:
128: /** Remove the wait decoration. */
129: public void dispose() {
130: if (timer != null) {
131: timer.stop();
132: timer = null;
133: }
134: super .dispose();
135: }
136:
137: /** First fade the background, then spin the dial. */
138: public void actionPerformed(ActionEvent e) {
139: if (fade < FADE_THRESHOLD) {
140: fade += 32;
141: if (fade >= FADE_THRESHOLD)
142: timer.setDelay(SpinningDial.SPIN_INTERVAL);
143: } else {
144: dial.nextFrame();
145: }
146: getPainter().repaint();
147: }
148: }
|