package org.limewire.ui.swing.components; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JWindow; import javax.swing.KeyStroke; import javax.swing.RootPaneContainer; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTargetAdapter; /** * An extension of JWindow that can be used as a popup window. PopupWindow * may be displayed using an animation that opens and closes the popup like a * window shade. * * Note that when a PopupWindow is opened, it installs a mouse listener on * the glass pane of its owner window. This handles mouse pressed events * to automatically close the popup. */ public class PopupWindow extends JWindow { private static final String CLOSE_ACTION_KEY = "closeWindow"; private static final int DURATION = 250; private static final int RESOLUTION = 30; private final OwnerListener ownerListener = new OwnerListener(); private boolean animated = true; private Animator animator; /** * Creates a window with no specified owner. */ public PopupWindow() { super(); initialize(); } /** * Creates a window with the specified owner frame. */ public PopupWindow(Frame owner) { super(owner); initialize(); } /** * Creates a window with the specified GraphicsConfiguration of a screen * device. */ public PopupWindow(GraphicsConfiguration gc) { super(gc); initialize(); } /** * Creates a window with the specified owner window. */ public PopupWindow(Window owner) { super(owner); initialize(); } /** * Creates a window with the specified owner window and GraphicsConfiguration * of a screen device. */ public PopupWindow(Window owner, GraphicsConfiguration gc) { super(owner, gc); initialize(); } /** * Initializes the window by installing listeners. */ private void initialize() { // Add window listener to install actions when opened, and clean up // listeners when closed. addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { uninstallInputActions(); Window owner = getOwner(); if (owner instanceof RootPaneContainer) { Component glassPane = ((RootPaneContainer) owner).getGlassPane(); owner.removeComponentListener(ownerListener); glassPane.removeMouseListener(ownerListener); glassPane.setVisible(false); } } @Override public void windowOpened(WindowEvent e) { installInputActions(); Window owner = getOwner(); if (owner instanceof RootPaneContainer) { Component glassPane = ((RootPaneContainer) owner).getGlassPane(); owner.addComponentListener(ownerListener); glassPane.addMouseListener(ownerListener); glassPane.setVisible(true); } } }); } /** * Installs input actions in content pane. */ private void installInputActions() { Container container = getContentPane(); if (container instanceof JComponent) { JComponent contentPane = (JComponent) container; // Create Escape key binding to close window. contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), CLOSE_ACTION_KEY); contentPane.getActionMap().put(CLOSE_ACTION_KEY, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { setVisible(false); } }); } } /** * Uninstalls input actions in content pane. */ private void uninstallInputActions() { Container container = getContentPane(); if (container instanceof JComponent) { JComponent contentPane = (JComponent) container; contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove( KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); contentPane.getActionMap().remove(CLOSE_ACTION_KEY); } } /** * Creates a popup window with the specified parent component, content * pane and screen location. */ public static PopupWindow createPopupWindow(JComponent parent, JComponent contentPane, Point location) { // Declare new popup window. PopupWindow popupWindow; // Create popup window for parent container. Container ancestor = parent.getTopLevelAncestor(); if (ancestor instanceof Frame) { popupWindow = new PopupWindow((Frame) ancestor); } else if (ancestor instanceof Window) { popupWindow = new PopupWindow((Window) ancestor); } else { popupWindow = new PopupWindow(); } // Calculate window size and location. popupWindow.setContentPane(contentPane); popupWindow.pack(); popupWindow.setLocation(location); // Return the window. return popupWindow; } /** * Sets an indicator to animate the popup window. The built-in animation * opens and closes the popup like a window shade. */ public void setAnimated(boolean animated) { this.animated = animated; } /** * Overrides superclass method to animate the display. */ @Override public void setVisible(boolean visible) { if (animated) { startAnimation(visible); } else { super.setVisible(visible); // Always call dispose() when hidden to ensure windowClosed event // is fired. The event is used to remove the owner listener. if (!visible) { dispose(); } } } /** * Starts an animation to show or hide the popup window. */ private void startAnimation(boolean visible) { if (isVisible() != visible) { // Stop existing animator. stopAnimator(); // Save preferred size. Dimension initialSize = visible ? getPreferredSize() : getSize(); // Make visible if requested with zero height. The animation will // change the height to make the window grow. if (!isVisible() && visible) { setSize(new Dimension(initialSize.width, 0)); super.setVisible(true); } // Create new animator and start. animator = new Animator(DURATION, new AnimationTarget(visible, initialSize)); animator.setResolution(RESOLUTION); animator.start(); } } /** * Stops the animation. */ private void stopAnimator() { if (animator != null) { animator.stop(); animator = null; } } /** * Listener to handle events on the popup owner. The popup is closed * when the owner is moved, or when the mouse is pressed on the owner's * glass pane. */ private class OwnerListener extends MouseAdapter implements ComponentListener { @Override public void componentHidden(ComponentEvent e) { dispose(); } @Override public void componentMoved(ComponentEvent e) { dispose(); } @Override public void componentResized(ComponentEvent e) { dispose(); } @Override public void componentShown(ComponentEvent e) { } @Override public void mousePressed(MouseEvent e) { setVisible(false); } } /** * Animation target to handle timing events. */ private class AnimationTarget extends TimingTargetAdapter { private final boolean makeVisible; private final Dimension initialSize; public AnimationTarget(boolean makeVisible, Dimension initialSize) { this.makeVisible = makeVisible; this.initialSize = initialSize; } @Override public void timingEvent(float fraction) { // Determine size percentage. float sizePct = makeVisible ? fraction : 1.0f - fraction; // Stop timer when we reach the end. if (makeVisible && sizePct > 0.98f) { stopAnimator(); sizePct = 1.0f; } else if (!makeVisible && sizePct < 0.02f) { stopAnimator(); sizePct = 0.0f; dispose(); } // Set window size. setSize(new Dimension(initialSize.width, (int) (initialSize.getHeight() * sizePct))); // Request repaint. repaint(); } } }