/* Copyright (c) 2006-2007 Timothy Wall, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* <p/>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package furbelow;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.Timer;
/* Gradually fade the display, then show a spinning dial indicator.
*/
public class SpinningDialWaitIndicator extends WaitIndicator implements ActionListener {
private static final int MAX_SIZE = 64;
private static final int FADE_INTERVAL = 1000/24;
private static final int FADE_THRESHOLD = 192;
// size of margins when space is sufficiently large
private static final int MARGIN = 8;
// margins take up 1/MARGIN_FRACTION when space is limited
private static final int MARGIN_FRACTION = 8;
private Timer timer;
private int fade;
private int verticalOffset;
private SpinningDial dial;
private String text;
public SpinningDialWaitIndicator(JFrame frame) {
this(frame.getLayeredPane());
JMenuBar mb = frame.getJMenuBar();
if (mb != null) {
// Exclude the menu bar from centering/sizing
verticalOffset = mb.getHeight();
}
}
public SpinningDialWaitIndicator(JComponent target) {
this(target, null);
}
public SpinningDialWaitIndicator(final JComponent target, String text) {
super(target);
this.text = text;
// Draw the dial centered and scaled to fit, up to a maximum size
dial = new SpinningDial() {
public int getIconWidth() {
Rectangle r = getComponent().getVisibleRect();
int margin = Math.min(MARGIN, r.width/MARGIN_FRACTION);
return Math.min(MAX_SIZE, r.width-margin*2);
}
public int getIconHeight() {
Rectangle r = getComponent().getVisibleRect();
int margin = Math.min(MARGIN, r.height/MARGIN_FRACTION);
return Math.min(MAX_SIZE, r.height-verticalOffset-margin*2);
}
};
// Disable automatic animation
dial.setFrameInterval(0);
}
public void setText(String text) {
this.text = text;
repaint();
}
/** Fade the affected component to background, then apply a spinning
* wait indicator.
*/
public void paint(Graphics graphics) {
if (timer == null) {
timer = new Timer(FADE_INTERVAL, this);
timer.start();
}
Graphics2D g = (Graphics2D)graphics.create();
Rectangle r = getComponent().getVisibleRect();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Color bg = getComponent().getBackground();
g.setColor(new Color(bg.getRed(), bg.getGreen(), bg.getBlue(), fade));
g.fillRect(r.x, r.y, r.width, r.height);
if (fade < FADE_THRESHOLD)
return;
int x = r.x;
if (text == null)
x += (r.width - dial.getIconWidth())/2;
else
x += dial.getIconWidth()/4;
int y = r.y + verticalOffset + (r.height - verticalOffset - dial.getIconHeight())/2;
dial.paintIcon(getPainter(), g, x, y);
if (text != null) {
Font font = g.getFont();
g.setFont(font.deriveFont(Font.BOLD, dial.getIconHeight()/2));
FontMetrics m = g.getFontMetrics();
x += dial.getIconWidth() * 5/4;
// FIXME vertical offset is not quite right
y += dial.getIconHeight() - (dial.getIconHeight() - m.getAscent())/2;
g.setColor(getComponent().getForeground());
g.drawString(text, x, y);
}
g.dispose();
}
/** Remove the wait decoration. */
public void dispose() {
if (timer != null) {
timer.stop();
timer = null;
}
super.dispose();
}
/** First fade the background, then spin the dial. */
public void actionPerformed(ActionEvent e) {
if (fade < FADE_THRESHOLD) {
fade += 32;
if (fade >= FADE_THRESHOLD)
timer.setDelay(SpinningDial.SPIN_INTERVAL);
}
else {
dial.nextFrame();
}
getPainter().repaint();
}
}