/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.layout; import java.awt.CardLayout; import java.awt.Component; import java.awt.Container; import java.util.Enumeration; import java.util.Hashtable; import javax.swing.SwingUtilities; /** * A CardLayout extensions, which animates the change from one card to another card with a fade animation (by using the * FadingPanel component). * * @author jruiz * @version $Revision$, $Date$ */ public class FadingCardLayout extends CardLayout { //~ Static fields/initializers --------------------------------------------- /** the cardname of the fade panel. */ private static final String FADEPANEL_NAME = "__fadePanel__"; //~ Instance fields -------------------------------------------------------- /** The default duration of the fade animation. */ private long fadeDuration = 1000; /** Maps the added components to their cardname. */ private Hashtable<String, Component> componentHashtable = new Hashtable<String, Component>(); /** the panel to show when the fade animation is finished. */ private Component fadeTo = null; /** this panel is used to render the fade animation. */ private FadingPanel fadePanel = null; //~ Constructors ----------------------------------------------------------- /** * Creates a new FadingCardLayout object. */ public FadingCardLayout() { super(); // neuen fadePanel erzeugen fadePanel = new FadingPanel(); // als Listener anmelden um informiert zu werden wenn das Faden beendet wurde fadePanel.addFadingPanelListener(new FadingPanelListener() { @Override public void fadeFinished() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final Container parent = fadePanel.getParent(); // ursprüngliches Panel zu dem gefadet wurde zeigen showWithoutFade(parent, getCardnameOf(fadeTo)); fadeTo = null; // fadePanel wieder aus dem Container entfernen parent.remove(fadePanel); } }); } }); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @param name DOCUMENT ME! * @param component DOCUMENT ME! */ @Override public void addLayoutComponent(final String name, final Component component) { componentHashtable.put(name, component); super.addLayoutComponent(name, component); } /** * DOCUMENT ME! * * @param component DOCUMENT ME! */ @Override public void removeLayoutComponent(final Component component) { final Enumeration<String> keys = componentHashtable.keys(); while (keys.hasMoreElements()) { final String key = keys.nextElement(); if (componentHashtable.get(key) == component) { componentHashtable.remove(key); } } super.removeLayoutComponent(component); } /** * DOCUMENT ME! * * @param parent DOCUMENT ME! */ @Override public void next(final Container parent) { synchronized (parent.getTreeLock()) { // current ist die momentan angezeigte komponente, oder aber fadeto falls gerade gefadet wird int currentIndex = (fadeTo != null) ? getIndexOfComponent(parent, fadeTo) : getIndexOfCurrentComponent(parent); // next ist die nächste komponente int nextIndex = ++currentIndex % parent.getComponentCount(); // ... beziehungsweise die übernächste falls die nächste der fadepanel ist nextIndex = (parent.getComponent(nextIndex) == fadePanel) ? (++nextIndex % parent.getComponentCount()) : nextIndex; // faden zur nächsten komponente fade(parent, parent.getComponent(nextIndex)); } } /** * DOCUMENT ME! * * @param parent DOCUMENT ME! */ @Override public void previous(final Container parent) { synchronized (parent.getTreeLock()) { // current ist die momentan angezeigte komponente, oder aber fadeto falls gerade gefadet wird int currentIndex = (fadeTo != null) ? getIndexOfComponent(parent, fadeTo) : getIndexOfCurrentComponent(parent); // previous ist die vorherige komponente int previousIndex = (currentIndex == 0) ? (parent.getComponentCount() - 1) : --currentIndex; // ... beziehungsweise die vor-vorherige falls die vorherige der fadepanel ist previousIndex = (parent.getComponent(previousIndex) == fadePanel) ? (--previousIndex % parent.getComponentCount()) : previousIndex; // faden zur vorherigen komponente fade(parent, parent.getComponent(previousIndex)); } } /** * DOCUMENT ME! * * @param parent DOCUMENT ME! */ @Override public void first(final Container parent) { synchronized (parent.getTreeLock()) { // first ist die erste komponente int firstIndex = 0; // ... beziehungsweise die nächste wenn die erste der fadepanel ist firstIndex = (parent.getComponent(firstIndex) == fadePanel) ? ++firstIndex : firstIndex; // faden zur ersten komponente fade(parent, parent.getComponent(firstIndex)); } } /** * DOCUMENT ME! * * @param parent DOCUMENT ME! */ @Override public void last(final Container parent) { synchronized (parent.getTreeLock()) { // last ist die letzte komponente int lastIndex = parent.getComponentCount() - 1; // ... beziehungsweise die vorherige wenn die letzte der fadepanel ist lastIndex = (parent.getComponent(lastIndex) == fadePanel) ? --lastIndex : lastIndex; // faden zur letzten komponente fade(parent, parent.getComponent(lastIndex)); } } /** * DOCUMENT ME! * * @param parent DOCUMENT ME! * @param name DOCUMENT ME! */ @Override public void show(final Container parent, final String name) { synchronized (parent.getTreeLock()) { fade(parent, componentHashtable.get(name)); } } /** * Shows the given card within the parent container, without to start the fade animation. * * @param parent the container * @param name the name of the card to show */ private void showWithoutFade(final Container parent, final String name) { super.show(parent, name); } /** * Fades the actualy shown component to the given component. The fade panel is shown and his animation is started. * * @param parent DOCUMENT ME! * @param fadeTo DOCUMENT ME! */ private void fade(final Container parent, final Component fadeTo) { // component holen von der aus gefadet werden soll final Component fadeFrom = getCurrentComponent(parent); // wenn nicht gefadet werden brauch (Quelle = Ziel) if (fadeFrom == fadeTo) { // dann "normal" zum Ziel wechseln (ohne zu faden). showWithoutFade(parent, getCardnameOf(fadeTo)); } else { // sonst => faden: // Ziel-Panel merken um es nach dem Faden setzen zu können (listener) this.fadeTo = fadeTo; // fadePanel dem parent temporär hinzufügen und anzeigen if (getCardnameOf(fadePanel) == null) { parent.add(fadePanel, FADEPANEL_NAME); } showWithoutFade(parent, FADEPANEL_NAME); // fade-Vorgang starten fadePanel.startFading(fadeFrom, fadeTo, fadeDuration); } } /** * Returns the card name of a component. * * @param component The component * * @return the card name of the component, or null if the component was not found within the container */ private String getCardnameOf(final Component component) { final Enumeration<String> keys = componentHashtable.keys(); while (keys.hasMoreElements()) { final String key = keys.nextElement(); if (componentHashtable.get(key) == component) { return key; } } return null; } /** * Returns the index of the first visible component within the given container. If the containers layout is a * cardlayout, then it will be the index of the currently shown component. * * @param parent the container * * @return the index of the first visible component, or -1 if no component is visible */ private static int getIndexOfCurrentComponent(final Container parent) { for (int index = 0; index < parent.getComponentCount(); index++) { final Component component = parent.getComponent(index); if (component.isVisible()) { return index; } } return -1; } /** * Returns the index of a given component, within a given container. * * @param parent the container * @param component the component * * @return the index of the component, or -1 when the component was not found */ private static int getIndexOfComponent(final Container parent, final Component component) { for (int index = 0; index < parent.getComponentCount(); index++) { if (parent.getComponent(index) == component) { return index; } } return -1; } /** * Returns the first visible component within a given container. * * @param parent the container * * @return the component, or null when no component is visible */ private static Component getCurrentComponent(final Container parent) { return parent.getComponent(getIndexOfCurrentComponent(parent)); } /** * DOCUMENT ME! * * @return the duration of the fade animation in ms */ public long getFadeDuration() { return fadeDuration; } /** * DOCUMENT ME! * * @param fadeDuration the duration of the fade animation in ms */ public void setFadeDuration(final long fadeDuration) { this.fadeDuration = fadeDuration; } }