/* * Copyright (C) 2011 Peransin Nicolas. * Use is subject to license terms. */ package org.mypsycho.swing; import java.awt.Component; import java.awt.Container; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; import java.util.List; /** * XXX Doc * <p>Detail ... </p> * @author Peransin Nicolas */ public abstract class ContainerPropagator { protected final Component root; protected final ContainerListener containListener = new ContainerListener() { public void componentAdded(ContainerEvent e) { activate(Event.ADD, e.getChild()); } public void componentRemoved(ContainerEvent e) { activate(Event.REMOVE, e.getChild()); } }; // Swing ?? protected final PropertyChangeListener propertyListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ((evt.getSource() instanceof Component) // ... && expectedProps.contains(evt.getPropertyName())) { update((Component) evt.getSource()); } } }; final List<String> expectedProps; public ContainerPropagator(String... expecteds) { this(null, expecteds); } public ContainerPropagator(Component c, String... expecteds) { root = c; expectedProps = Arrays.asList(expecteds.clone()); if (root != null) { register(root); } } public void update() { if (root == null) { throw new UnsupportedOperationException("root not defined"); } activate(Event.UPDATE, root); } /** * Disengage the root component and stop the listening. * <p> * This function must be called directly only when root != null. * </p> * * @param target the component to manage * @exception UnsupportedOperationException if the propagator is not create * with a root */ public void dispose() throws UnsupportedOperationException { if (root == null) { throw new UnsupportedOperationException("Undefined root"); } dispose(root); } /** * Engage the component and start the listening. * <p> * This function should be called directly only when root = null. * </p> * * @param target the component to manage */ public void register(Component target) { activate(Event.ADD, target); } /** * Engage the component and start the listening. * <p> * This function should be called directly only when root = null. * </p> * * @param targets the component to manage */ public void register(Component... targets) { for (Component target : targets) { register(target); } } /** * Engage the component and start the listening. * <p> * This function should be called directly only when root = null. * </p> * * @param targets the component to manage */ public void register(Iterable<Component> targets) { for (Component target : targets) { register(target); } } /** * Engage the component and start the listening. * <p> * This function should be called directly only when root = null. * </p> * * @param target the component to manage */ public void update(Component target) { activate(Event.UPDATE, target); } /** * Disengage the component and stop the listening. * <p> * This function should be called directly only when root = null. * </p> * * @param target the component to manage */ public void dispose(Component target) { // should be called directly only when root = null activate(Event.REMOVE, target); } /** * This method is not called several times on the same component without * clearComponent. * <p> * Children are automatically added. * </p> * * @param target added component */ protected void componentAdding(Component target) { componentUpdating(target); } /** * Can be called several times on the same component without clearComponent. * * @param target updated component */ protected void componentUpdating(Component target) {} /** * This method is called when a component is removed. * <p> * Children are automatically removed. * </p> * * @param target removed component */ protected void componentRemoving(Component target) {} /** * This method is not called several times on the same component without * clearComponent. * <p> * Children are automatically added. * </p> * * @param target added component */ protected void componentAdded(Component target) { componentUpdated(target); } /** * Can be called several times on the same component without clearComponent. * * @param target updated component */ protected void componentUpdated(Component target) {} /** * This method is called when a component is removed. * <p> * Children are automatically removed. * </p> * * @param target removed component */ protected void componentRemoved(Component target) {} protected enum Event { ADD { @Override void run(ContainerPropagator p, Component target, boolean pre) { if (pre) { p.componentAdding(target); } else { p.componentAdded(target); if (!p.expectedProps.isEmpty()) { target.addPropertyChangeListener(p.propertyListener); } } } }, REMOVE { @Override void run(ContainerPropagator p, Component target, boolean pre) { if (pre) { if (!p.expectedProps.isEmpty()) { target.removePropertyChangeListener(p.propertyListener); } p.componentRemoving(target); } else { p.componentRemoved(target); } } }, UPDATE { @Override void run(ContainerPropagator p, Component target, boolean pre) { if (pre) { p.componentUpdating(target); } else { p.componentUpdated(target); } } }; abstract void run(ContainerPropagator p, Component target, boolean after); } private void activate(Event act, Component target) { act.run(this, target, true); if (target instanceof Container) { Container cont = (Container) target; if (act == Event.ADD) { cont.addContainerListener(containListener); } else if (act == Event.REMOVE) { cont.removeContainerListener(containListener); } // Propagate the for (int iComp = 0; iComp < cont.getComponentCount(); iComp++) { activate(act, cont.getComponent(iComp)); } } act.run(this, target, false); } }