com.limegroup.gnutella.gui.notify.AnimatedWindow.java Source code

Java tutorial

Introduction

Here is the source code for com.limegroup.gnutella.gui.notify.AnimatedWindow.java

Source

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 com.limegroup.gnutella.gui.notify;

import java.awt.AWTException;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import javax.swing.UIManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AnimatedWindow extends JWindow {

    /**
     * 
     */
    private static final long serialVersionUID = 1764619610298463956L;

    private final static int ANIMATION_INTERVAL = 10;

    private final static int MOVE_PER_INTERVAL = 1;

    private final static float FADE_PER_INTERVAL = 0.015f;

    private static final Log LOG = LogFactory.getLog(AnimatedWindow.class);

    public static final Color TRANSPARENT = new Color(255, 255, 255, 0);

    public enum AnimationType {
        SHOW, HIDE
    };

    public enum AnimationMode {
        TOP_TO_BOTTOM, BOTTOM_TO_TOP, FADE;

        public boolean movesWindow() {
            switch (this) {
            case TOP_TO_BOTTOM:
            case BOTTOM_TO_TOP:
                return true;
            case FADE:
                return false;
            default:
                throw new RuntimeException("Unknown mode: " + this);
            }
        }

        public boolean needsBackgroundImage() {
            switch (this) {
            case FADE:
                return true;
            default:
                return false;
            }
        }

        public void initializeWindow(AnimatedWindow window, Point location) {
            Dimension preferredSize = window.getPreferredSize();

            switch (this) {
            case BOTTOM_TO_TOP:
                // minimizes flickering: the window is resized by calls to
                // setBounds() which immediately cause a resize of the peer window
                // filling the empty space with the peer background color before
                // paint() is invoked on any contained lightweight components.
                if (window.getContentBackground() != null) {
                    window.setBackground(window.getContentBackground());
                }

                // start animation with a small initial height
                window.setBounds(location.x, location.y + preferredSize.height - window.getInitialHeight(),
                        preferredSize.width, window.getInitialHeight());
                window.setOpacity2(1.0f);
                break;
            case TOP_TO_BOTTOM:
                // see above
                if (window.getContentBackground() != null) {
                    window.setBackground(window.getContentBackground());
                }

                // see above
                window.setBounds(location.x, location.y, preferredSize.width, window.getInitialHeight());
                window.setOpacity2(1.0f);
                break;
            case FADE:
                window.setBounds(location.x, location.y, preferredSize.width, preferredSize.height);
                window.setOpacity2(0.0f);
                window.setBackground(TRANSPARENT);
                break;
            }
        }

        public boolean animateShow(AnimatedWindow window, Point location) {
            Dimension preferredSize = window.getPreferredSize();
            switch (this) {
            case BOTTOM_TO_TOP:
                if (window.getHeight() >= preferredSize.height) {
                    return true;
                }

                int newHeight = Math.min(window.getHeight() + MOVE_PER_INTERVAL, preferredSize.height);
                window.setBounds(location.x, location.y + preferredSize.height - newHeight, preferredSize.width,
                        newHeight);
                // invoking repaint() is not sufficient since
                // mainPanel needs to be resized
                window.validate();
                return false;
            case TOP_TO_BOTTOM:
                if (window.getHeight() >= preferredSize.height) {
                    return true;
                }

                window.setBounds(location.x, location.y, preferredSize.width,
                        Math.min(window.getHeight() + MOVE_PER_INTERVAL, preferredSize.height));
                // invoking repaint() is not sufficient since
                // mainPanel needs to be resized
                window.validate();
                return false;
            case FADE:
                if (window.getOpacity2() >= 1.0f) {
                    return true;
                }
                window.setOpacity2(Math.min(window.getOpacity2() + FADE_PER_INTERVAL, 1.0f));
                window.repaint();
                return false;
            default:
                return true;
            }
        }

        public boolean animateHide(AnimatedWindow window, Point location) {
            Dimension preferredSize = window.getPreferredSize();

            switch (this) {
            case BOTTOM_TO_TOP:
                if (window.getHeight() == 0) {
                    return true;
                }

                int newHeight = Math.max(window.getHeight() - MOVE_PER_INTERVAL, 0);
                try {
                    window.setBounds(location.x, location.y + preferredSize.height - newHeight, preferredSize.width,
                            newHeight);
                } catch (IllegalArgumentException ignored) {
                    // See: LWC-1167.
                    LOG.warn("IAE setting bounds", ignored);
                }
                window.validate();
                return false;
            case TOP_TO_BOTTOM:
                if (window.getHeight() == 0) {
                    return true;
                }

                window.setSize(preferredSize.width, Math.max(window.getHeight() - MOVE_PER_INTERVAL, 0));
                window.validate();
                return false;
            case FADE:
                if (window.getOpacity2() == 0.0f) {
                    return true;
                }
                window.setOpacity2(Math.max(window.getOpacity2() - FADE_PER_INTERVAL, 0.0f));
                window.repaint();
                return false;
            default:
                return true;
            }
        }

        public void paint(AnimatedWindow window, Graphics g, BufferedImage backgroundImage,
                BufferedImage animationImage) {
            switch (this) {
            case BOTTOM_TO_TOP:
                g.drawImage(animationImage, 0, 0, null);
                break;
            case TOP_TO_BOTTOM:
                g.drawImage(animationImage, 0, window.getHeight() - window.getPreferredSize().height, null);
                break;
            case FADE:
                if (backgroundImage != null) {
                    g.drawImage(backgroundImage, 0, 0, null);
                } else {
                    g.setColor(Color.white);
                    g.fillRect(0, 0, window.getWidth(), window.getHeight());
                }
                Graphics2D gFade = (Graphics2D) g.create();
                AlphaComposite newComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                        window.getOpacity2());
                gFade.setComposite(newComposite);
                gFade.drawImage(animationImage, 0, 0, null);
                gFade.dispose();
                break;
            }
        }
    };

    /**
     * If true, the animation will display window from bottom to top. If false,
     * window will be displayed from top to bottom.
     */
    private AnimationMode mode = AnimationMode.BOTTOM_TO_TOP;

    /**
     * Timer used to for the show/hide animation.
     */
    private Timer animationTimer;

    private BufferedImage animationImage;

    private boolean hideOnClick;

    private int initialHeight;

    private Point finalLocation;

    private float opacity;

    private BufferedImage backgroundImage;

    private List<AnimatedWindowListener> listeners;

    private AnimationType currentAnimation;

    private Container contentPane;

    public AnimatedWindow(Window parent) {
        super(parent);
    }

    public Color getContentBackground() {
        return getContentPane().getBackground();
    }

    protected void autoHideWindow() {
        doHide();
    }

    public void doHide() {
        if (currentAnimation == AnimationType.HIDE) {
            return;
        }

        stopAnimations(true);

        final Point location = getLocation();

        prepareAnimation(location);
        currentAnimation = AnimationType.HIDE;

        animationTimer = new javax.swing.Timer(ANIMATION_INTERVAL, new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                boolean done = mode.animateHide(AnimatedWindow.this, location);
                if (done) {
                    currentAnimation = null;
                    animationTimer.stop();

                    disposeAnimation();

                    setVisible(false);

                    fireAnimationCompleted(AnimationType.HIDE);
                }
            }
        });
        animationTimer.setRepeats(true);
        animationTimer.start();
    }

    public void doShow() {
        if (currentAnimation == AnimationType.SHOW) {
            return;
        }

        stopAnimations(true);

        final Dimension preferredSize = getPreferredSize();
        final Point location = getFinalLocation();

        prepareAnimation(location);

        if (!isVisible()) {
            mode.initializeWindow(this, location);

            this.setVisible(true);
        }

        currentAnimation = AnimationType.SHOW;

        animationTimer = new javax.swing.Timer(ANIMATION_INTERVAL, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boolean done = mode.animateShow(AnimatedWindow.this, location);
                if (done) {
                    // make sure window is displayed at final location
                    if (location.y != getLocation().y || preferredSize.height != getSize().getHeight()) {
                        setBounds(location.x, location.y, preferredSize.width, preferredSize.height);
                    }

                    animationImage = null;

                    stopAnimations(false);

                    // ask Swing to paint the actual components
                    validate();

                    fireAnimationCompleted(AnimationType.SHOW);
                }
            }
        });
        animationTimer.setRepeats(true);
        animationTimer.start();
    }

    public int getInitialHeight() {
        return initialHeight;
    }

    public void setInitialHeight(int initialHeight) {
        this.initialHeight = initialHeight;
    }

    private void prepareAnimation(Point location) {
        if (animationImage != null) {
            // already have an image, this could be the case if a hide animation
            // is started while a show animation is still in progress or the
            // other
            // way around
            return;
        }

        // reset window to original size to capture all content
        pack();
        Dimension size = getSize();

        if (mode.needsBackgroundImage() && backgroundImage == null) {
            try {
                Robot robot = new Robot();
                backgroundImage = robot.createScreenCapture(new Rectangle(location, size));
            } catch (AWTException e) {
                LOG.warn("Could not capture background image", e);
                backgroundImage = null;
            }
        }

        animationImage = getGraphicsConfiguration().createCompatibleImage(size.width, size.height);
        Graphics grahpics = animationImage.getGraphics();
        getContentPane().paint(grahpics);
        grahpics.dispose();
    }

    private void disposeAnimation() {
        animationImage = null;
        backgroundImage = null;
    }

    /**
     * Returns the lower right corner of the screen.
     */
    public Point getDefaultParentLocation() {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        return new Point(screenSize.width - 1, screenSize.height - 1);
    }

    /**
     * Returns (0, 0).
     */
    public Dimension getDefaultParentSize() {
        return new Dimension(0, 0);
    }

    /**
     * Hides the window slowly using an animation.
     */
    public void hideWindow() {
        doHide();
    }

    /**
     * Hides the window immediately not using an animation.
     */
    public void hideWindowImmediately() {
        stopAnimations(true);

        setVisible(false);

        disposeAnimation();
    }

    @Override
    public Container getContentPane() {
        if (this.contentPane == null) {
            setContentPane(new JPanel());
        }
        return this.contentPane;
    }

    @Override
    public void setContentPane(Container contentPane) {
        this.contentPane = contentPane;
        JPanel panel = new JPanel(new BorderLayout()) {
            /**
             * 
             */
            private static final long serialVersionUID = 1025231305407376307L;

            @Override
            public void paint(Graphics g) {
                if (animationImage != null && isAnimationInProgress()) {
                    mode.paint(AnimatedWindow.this, g, backgroundImage, animationImage);
                } else {
                    super.paint(g);
                }
            }
        };
        panel.setDoubleBuffered(true);
        panel.add(contentPane, BorderLayout.CENTER);
        super.setContentPane(panel);
    }

    public boolean isAnimationInProgress() {
        return currentAnimation != null;
    }

    public boolean isHideAnimationInProgress() {
        return currentAnimation == AnimationType.HIDE;
    }

    public boolean isShowAnimationInProgress() {
        return currentAnimation == AnimationType.SHOW;
    }

    private void stopAnimations(boolean notify) {
        if (currentAnimation == null) {
            return;
        }

        currentAnimation = null;

        if (animationTimer != null) {
            animationTimer.stop();
        }

        if (notify) {
            fireAnimationStopped(currentAnimation);
        }
    }

    protected void fireAnimationStopped(AnimationType type) {
        if (listeners != null) {
            AnimatedWindowEvent event = new AnimatedWindowEvent(this, type);
            for (AnimatedWindowListener listener : listeners) {
                listener.animationStopped(event);
            }
        }
    }

    protected void fireAnimationCompleted(AnimationType type) {
        if (listeners != null) {
            AnimatedWindowEvent event = new AnimatedWindowEvent(this, type);
            for (AnimatedWindowListener listener : listeners) {
                listener.animationCompleted(event);
            }
        }
    }

    public boolean getHideOnClick() {
        return hideOnClick;
    }

    public void setHideOnClick(boolean hideOnClick) {
        this.hideOnClick = hideOnClick;
    }

    public AnimationMode getMode() {
        return mode;
    }

    public void setMode(AnimationMode mode) {
        if (mode == null) {
            throw new IllegalArgumentException();
        }

        this.mode = mode;
    }

    public static void main(String[] args) {
        JPanel content = new JPanel(new BorderLayout());
        content.setBorder(BorderFactory.createLineBorder(Color.black, 2));
        JLabel label = new JLabel("Hello World");
        label.setIcon(UIManager.getIcon("FileView.computerIcon"));
        label.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        content.add(label, BorderLayout.CENTER);

        final AnimatedWindow window = new AnimatedWindow(null);
        window.setFinalLocation(new Point(200, 200));
        window.setContentPane(content);

        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 10, 10));

        JButton button = new JButton("Bottom -> Top");
        buttonPanel.add(button);
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                window.setMode(AnimationMode.BOTTOM_TO_TOP);
                if (!window.isVisible() || window.isHideAnimationInProgress()) {
                    window.doShow();
                } else {
                    window.doHide();
                }
            }
        });

        button = new JButton("Top -> Bottom");
        buttonPanel.add(button);
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                window.setMode(AnimationMode.TOP_TO_BOTTOM);
                if (!window.isVisible() || window.isHideAnimationInProgress()) {
                    window.doShow();
                } else {
                    window.doHide();
                }
            }
        });

        button = new JButton("Fade");
        buttonPanel.add(button);
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                window.setMode(AnimationMode.FADE);
                if (!window.isVisible() || window.isHideAnimationInProgress()) {
                    window.doShow();
                } else {
                    window.doHide();
                }
            }
        });

        JFrame app = new JFrame("AnimatedWindow Demo");
        app.setContentPane(buttonPanel);
        app.pack();
        app.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        app.setVisible(true);
    }

    public Point getFinalLocation() {
        return finalLocation;
    }

    public void setFinalLocation(Point finalLocation) {
        this.finalLocation = finalLocation;
    }

    public float getOpacity2() {
        return opacity;
    }

    public void setOpacity2(float opacity) {
        if (opacity < 0.0f || opacity > 1.0f) {
            throw new IllegalArgumentException();
        }

        this.opacity = opacity;
    }

    public void addAnimatedWindowListener(AnimatedWindowListener listener) {
        if (listeners == null) {
            listeners = new ArrayList<AnimatedWindowListener>();
        }
        listeners.add(listener);
    }

    public void removeAnimatedWindowListener(AnimatedWindowListener listener) {
        if (listeners != null) {
            listeners.remove(listener);
        }
    }
}