/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2011-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2011-2012, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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 org.geotoolkit.internal.swing;
import java.awt.Window;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import javax.swing.JInternalFrame;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
import org.apache.sis.util.Disposable;
import org.apache.sis.util.ArraysExt;
/**
* A listener for AWT/Swing {@link Window} or {@link JInternalFrame} which dispose every
* component implementing the {@link Disposable} interface when the window is closed. In
* order to install the listeners only when first needed, use the following code:
*
* {@preformat java
* addAncestorListener(ComponentDisposer.INSTANCE);
* }
*
* This listener provides a convenient way to dispose resources in a way that reduce the
* risk of memory leaks, since we never store direct references to the child components
* to dispose.
* <p>
* Note that in order to get the resources disposed properly when the window is closed,
* the window default close operation shall be {@code DISPOSE_ON_CLOSE} rather than
* {@code EXIT_ON_CLOSE}.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.17
*
* @since 3.17
* @module
*/
public final class ComponentDisposer extends WindowAdapter implements InternalFrameListener, AncestorListener {
/**
* The singleton instance. See class javadoc for usage.
*/
public static final ComponentDisposer INSTANCE = new ComponentDisposer();
/**
* Do not allow instantiation except the singleton.
*/
private ComponentDisposer() {
}
/**
* Installs the window listeners in the ancestor of the component specified by the given
* event. This method does nothing if the window listeners are already installed.
*
* @param event The event from which to get the component for which to install the listeners.
*/
@Override
public void ancestorAdded(final AncestorEvent event) {
event.getComponent().removeAncestorListener(this); // No need to call this method again.
Container container = event.getAncestor();
while (container != null) {
if (container instanceof JInternalFrame) {
final JInternalFrame window = (JInternalFrame) container;
if (!ArraysExt.containsIdentity(window.getInternalFrameListeners(), INSTANCE)) {
window.addInternalFrameListener(INSTANCE);
}
}
if (container instanceof Window) {
final Window window = (Window) container;
if (!ArraysExt.containsIdentity(window.getWindowListeners(), INSTANCE)) {
window.addWindowListener(INSTANCE);
}
}
container = container.getParent();
}
}
/**
* Invoked when a window or an internal frame is closed. This method will call the
* {@link Disposable#dispose()} method for every child components which implement
* the {@link Disposable} interface. This method searches recursively in all children.
*/
private void dispose(final Container container) {
for (final Component component : container.getComponents()) {
if (component instanceof Container) {
dispose((Container) component);
}
if (component instanceof Disposable) {
((Disposable) component).dispose();
}
}
}
/**
* Invoked when the AWT window has been closed.
*
* @param event The event from which to get the closed window.
*/
@Override
public void windowClosed(final WindowEvent event) {
dispose(event.getWindow());
}
/**
* Invoked when the Swing internal frame has been closed.
*
* @param event The event from which to get the closed frame.
*/
@Override
public void internalFrameClosed(final InternalFrameEvent event) {
dispose(event.getInternalFrame());
}
@Override public void ancestorMoved (final AncestorEvent event) {}
@Override public void ancestorRemoved (final AncestorEvent event) {}
@Override public void internalFrameOpened (final InternalFrameEvent event) {}
@Override public void internalFrameIconified (final InternalFrameEvent event) {}
@Override public void internalFrameDeiconified(final InternalFrameEvent event) {}
@Override public void internalFrameActivated (final InternalFrameEvent event) {}
@Override public void internalFrameDeactivated(final InternalFrameEvent event) {}
@Override public void internalFrameClosing (final InternalFrameEvent event) {}
}