/**
* GmailAssistant 1.1 (2008-03-16)
* Copyright 2008 Zach Scrivena
* zachscrivena@gmail.com
* http://gmailassistant.sourceforge.net/
*
* Notifier for multiple Gmail accounts.
*
* TERMS AND CONDITIONS:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gmailassistant;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.JWindow;
import javax.swing.Timer;
/**
* Display a sliding animation for a window.
*/
class SlidingAnimator
extends JWindow
implements ActionListener
{
/** interval between animation refreshes */
private static final int ANIMATION_REFRESH_INTERVAL_MILLISECONDS = 20;
/** source window to be animated */
private final JWindow sourceWindow;
/** direction of sliding animation */
private final SlidingDirection dir;
/** Swing timer for animation */
private final Timer swingTimer;
/** buffered off-screen image of the source window */
private final BufferedImage img;
/** is the animation running? */
private volatile boolean running;
/** is this the first step of the animation? */
boolean firstStep;
/** source window width */
private final int sourceWidth;
/** source window height */
private final int sourceHeight;
/** x-coordinate of the canvas */
private int canvasX;
/** y-coordinate of the canvas */
private int canvasY;
/** final x-coordinate of the window */
private final int finalX;
/** final y-coordinate of the window */
private final int finalY;
/** current x-coordinate of the window */
private int currentX;
/** current y-coordinate of the window */
private int currentY;
/** increment for the x-coordinate of the window */
private final int deltaX;
/** increment for the y-coordinate of the window */
private final int deltaY;
/** width of the animation window */
private int windowWidth;
/** height of the animation window */
private int windowHeight;
/** x-coordinate of the animation window */
private int windowX;
/** y-coordinate of the animation window */
private int windowY;
/**
* Constructor. This method must run on the EDT.
* The source window does not need to be visible when calling the function.
*
* @param sourceWindow
* source window to be animated
* @param sourceWidth
* width of the source window
* @param sourceHeight
* height of the source window
* @param canvasX
* x-coordinate of the canvas on which to animate
* @param canvasY
* y-coordinate of the canvas on which to animate
* @param stepSize
* number of incremental pixels per animation step
* @param dir
* direction of animation
*/
SlidingAnimator(
final JWindow sourceWindow,
final int sourceWidth,
final int sourceHeight,
final int canvasX,
final int canvasY,
final int stepSize,
final SlidingDirection dir)
{
this.sourceWindow = sourceWindow;
this.sourceWidth = sourceWidth;
this.sourceHeight = sourceHeight;
this.canvasX = canvasX;
this.canvasY = canvasY;
this.dir = dir;
this.setAlwaysOnTop(true);
/* create an off-screen buffered image of the source window */
img = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_INT_RGB);
final Graphics2D g = img.createGraphics();
sourceWindow.setVisible(true);
sourceWindow.paintAll(g);
if ((dir == SlidingDirection.UP_IN) ||
(dir == SlidingDirection.DOWN_IN) ||
(dir == SlidingDirection.LEFT_IN) ||
(dir == SlidingDirection.RIGHT_IN))
{
sourceWindow.setVisible(false);
}
final int step = (stepSize == 0) ? 1 : Math.abs(stepSize);
/* determine animation steps */
switch (dir)
{
case UP_IN:
currentX = canvasX;
currentY = canvasY + sourceHeight;
finalX = canvasX;
finalY = canvasY;
deltaX = 0;
deltaY = -step;
break;
case DOWN_IN:
currentX = canvasX;
currentY = canvasY - sourceHeight;
finalX = canvasX;
finalY = canvasY;
deltaX = 0;
deltaY = step;
break;
case LEFT_IN:
currentX = canvasX + sourceWidth;
currentY = canvasY;
finalX = canvasX;
finalY = canvasY;
deltaX = -step;
deltaY = 0;
break;
case RIGHT_IN:
currentX = canvasX - sourceWidth;
currentY = canvasY;
finalX = canvasX;
finalY = canvasY;
deltaX = step;
deltaY = 0;
break;
case UP_OUT:
currentX = canvasX;
currentY = canvasY;
finalX = canvasX;
finalY = canvasY - sourceHeight;
deltaX = 0;
deltaY = -step;
break;
case DOWN_OUT:
currentX = canvasX;
currentY = canvasY;
finalX = canvasX;
finalY = canvasY + sourceHeight;
deltaX = 0;
deltaY = step;
break;
case LEFT_OUT:
currentX = canvasX;
currentY = canvasY;
finalX = canvasX - sourceWidth;
finalY = canvasY;
deltaX = -step;
deltaY = 0;
break;
case RIGHT_OUT:
currentX = canvasX;
currentY = canvasY;
finalX = canvasX + sourceWidth;
finalY = canvasY;
deltaX = step;
deltaY = 0;
break;
default:
finalX = currentX;
finalY = currentY;
deltaX = 0;
deltaY = 0;
}
swingTimer = new Timer(SlidingAnimator.ANIMATION_REFRESH_INTERVAL_MILLISECONDS, this);
}
/**
* Perform the sliding animation, blocking until the animation is completed.
* This method should NOT run on the EDT.
*/
void animate()
{
running = true;
firstStep = true;
swingTimer.start();
while (true)
{
if (!running)
{
break;
}
try
{
Thread.sleep(SlidingAnimator.ANIMATION_REFRESH_INTERVAL_MILLISECONDS);
}
catch (Exception e)
{
/* ignore */
}
}
/* animation is completed */
if ((dir == SlidingDirection.UP_IN) ||
(dir == SlidingDirection.DOWN_IN) ||
(dir == SlidingDirection.LEFT_IN) ||
(dir == SlidingDirection.RIGHT_IN))
{
sourceWindow.setVisible(true);
}
else
{
sourceWindow.setVisible(false);
}
setVisible(false);
}
/**
* Called by the Swing timer periodically.
* This method runs on the EDT.
*/
public void actionPerformed(ActionEvent e)
{
final int nextX = currentX + deltaX;
final int nextY = currentY + deltaY;
boolean proceed = true;
if ((dir == SlidingDirection.UP_IN) ||
(dir == SlidingDirection.UP_OUT))
{
if (nextY < finalY)
{
proceed = false;
}
}
else if ((dir == SlidingDirection.DOWN_IN) ||
(dir == SlidingDirection.DOWN_OUT))
{
if (nextY > finalY)
{
proceed = false;
}
}
else if ((dir == SlidingDirection.LEFT_IN) ||
(dir == SlidingDirection.LEFT_OUT))
{
if (nextX < finalX)
{
proceed = false;
}
}
else if ((dir == SlidingDirection.RIGHT_IN) ||
(dir == SlidingDirection.RIGHT_OUT))
{
if (nextX > finalX)
{
proceed = false;
}
}
if (proceed)
{
currentX = nextX;
currentY = nextY;
}
else
{
running = false;
swingTimer.stop();
return;
}
if (currentX >= canvasX)
{
windowX = currentX;
windowWidth = sourceWidth - currentX + canvasX;
}
else
{
windowX = canvasX;
windowWidth = currentX + sourceWidth - canvasX;
}
if (currentY >= canvasY)
{
windowY = currentY;
windowHeight = sourceHeight - currentY + canvasY;
}
else
{
windowY = canvasY;
windowHeight = currentY + sourceHeight - canvasY;
}
setSize(windowWidth, windowHeight);
setLocation(windowX, windowY);
if (firstStep)
{
setVisible(true);
firstStep = false;
if ((dir == SlidingDirection.UP_OUT) ||
(dir == SlidingDirection.DOWN_OUT) ||
(dir == SlidingDirection.LEFT_OUT) ||
(dir == SlidingDirection.RIGHT_OUT))
{
sourceWindow.setVisible(false);
}
}
}
@Override
public void update(Graphics g)
{
paint(g);
}
@Override
public void paint(Graphics g)
{
g.drawImage(img.getSubimage(
windowX - currentX,
windowY - currentY,
windowWidth,
windowHeight),
0,
0,
this);
}
/*****************
* INNER CLASSES *
*****************/
/**
* Sliding animation direction.
*/
public enum SlidingDirection
{
UP_IN,
UP_OUT,
DOWN_IN,
DOWN_OUT,
LEFT_IN,
LEFT_OUT,
RIGHT_IN,
RIGHT_OUT
}
}