import java.awt.*;
import java.awt.event.*;

/** Helper to add appropriate listeners to Components.
 * (For those who miss the 1.0.2 coding style)
 *
 * Called by a Frame or Dialog or Panel subclass like this --
 * ListenerAdder.add_Listeners (this, this)
 * will attempt to add listeners for all descendants.
 *
 * May be called this way to get menubar and "all others"
 * ListenerAdder.add_Listeners (this, menubar);
 * ListenerAdder.add_Listeners (this, this);
 *
 * COULD complain if unable to add a listener,
 * e.g. by throwing IllegalArgumentException,
 * since that is probably a bug,
 * caller can have an empty listener if really no interest.
 * But not with our recursive listening on Containers,
 * quite likely to have things the caller does not care about.
 *
 * @author Morris Hirsch
 * IPD Inc Newport RI */

public class ListenerAdder {

  public static String getVersion () { return "$Id: ListenerAdder.java,v 1.3 1998/09/30 14:25:40 mhirsch Exp $"; }

/** Called by a Frame or Dialog to add listeners to their Components.
 * A common idiom is ListenerAdder.add_Listeners (this, this),
 * by which a Container registers to listen on all its children.
 * @param caller -- Calling Frame or Dialog that will be the listener.
 * @param root -- where to begin expansion of widgets to listen to.
 */

/* The order of tests does matter,
 * because some earlier tests are subclasses of later ones. */

  public static void add_Listeners (Component caller, Object root) {

//System.out.println ("add_Listeners "+caller+" "+root);

/* Special for add_Listeners (this, this) by a Container */

	if ((caller == root) && (root instanceof Container)) {

/* Forward any WindowEvent invocations the class can handle,
 * and ignore all the rest.
 * For example,
 * some dialog might have only this --
 *
 * public void windowClosing (WindowEvent we) { dispose (); }
 *
 * in order to respond to the window frame close button.
 * Don't bother if no listener methods are implemented. */

	    if (caller instanceof Window) {
		WindowEventForwarder wef
		 = new WindowEventForwarder ((Window)caller);
		 if (wef.isUseful ())
		    ((Window)caller).addWindowListener (wef);
	    }

/* Listen to all the children,
 * recursively for any that are also Containers.
 * Also listen for and forward to the Container,
 * any focus changes among children.
 * Don't bother if no listener methods are implemented. */

	    Container C = (Container)caller;

            FocusEventForwarder fef
	     = new FocusEventForwarder (C);

	    for (int ii = 0; ii < C.getComponentCount (); ii++) {
		Component aComp = C.getComponent (ii);
		if (fef.isUseful ())
		    aComp.addFocusListener (fef);
		add_Listeners (caller, aComp);
	    }
	}

	else if (root instanceof MenuBar) {
	    MenuBar MB = (MenuBar)root;
	    for (int ii = 0; ii < MB.getMenuCount (); ii++)
		add_Listeners (caller, MB.getMenu (ii));
	}

/* Menu other than MenuBar */

	else if (root instanceof Menu) {
	    Menu M = (Menu)root;
	    for (int ii = 0; ii < M.getItemCount (); ii++)
		add_Listeners (caller, M.getItem (ii));
	}

/* List produces both Item and Action Events */

	else if (root instanceof List) {
	    ItemSelectable SI = (ItemSelectable)root;
	    if (caller instanceof ItemListener)
	        SI.addItemListener ((ItemListener)caller);

	    List L = (List)root;
	    if (caller instanceof ActionListener)
	        L.addActionListener ((ActionListener)caller);
	}

/* ItemSelectable other than List,
 * this includes Checkbox and CheckboxMenuItem,
 * also CheckImageButton. */

	else if (root instanceof ItemSelectable) {
	    ItemSelectable SI = (ItemSelectable)root;
	    if (caller instanceof ItemListener)
	        SI.addItemListener ((ItemListener)caller);
	}

/* MenuItem not CheckboxMenuItem */

	else if (root instanceof MenuItem) {
	    MenuItem MI = (MenuItem)root;
	    if (caller instanceof ActionListener)
	        MI.addActionListener ((ActionListener)caller);
	}

/* There should be a tagging interface for Action producers!
 * Buttons and things that act like Buttons.
 * Since there is not,
 * we find out by reflection,
 * if this class has an addActionListener method. */

	else if (null != (addActionListenerMethod
	 = getAddActionListenerMethod (root))) {
	    if (caller instanceof ActionListener) {
	        Object []params = { caller };
	        try { addActionListenerMethod.invoke (root, params); }
		catch (Exception ex) {}
	    }
	}

	else if (root instanceof Adjustable) {
	    Adjustable A = (Adjustable)root;
	    if (caller instanceof AdjustmentListener)
	        A.addAdjustmentListener ((AdjustmentListener)caller);
	}

/* If the Container is a "composite widget" for one of the above,
 * we have already added any suitable listener and we are done,
 * otherwise let caller listen on the contained children. */

	else if (root instanceof Container) {
	    Container C = (Container)root;
	    for (int ii = 0; ii < C.getComponentCount (); ii++)
		add_Listeners (caller, C.getComponent (ii));
	}
    }

  public static void remove_Listeners (Component caller, Object root) {

/* Special for remove_Listeners (this, this) */

	if ((caller == root) && (root instanceof Container)) {
	    Container C = (Container)root;
	    for (int ii = 0; ii < C.getComponentCount (); ii++)
		remove_Listeners (caller, C.getComponent (ii));
	}

	else if (root instanceof MenuBar) {
	    MenuBar MB = (MenuBar)root;
	    for (int ii = 0; ii < MB.getMenuCount (); ii++)
		remove_Listeners (caller, MB.getMenu (ii));
	}

	else if (root instanceof Menu) {
	    Menu M = (Menu)root;
	    for (int ii = 0; ii < M.getItemCount (); ii++)
		remove_Listeners (caller, M.getItem (ii));
	}

	else if (root instanceof List) {
	    ItemSelectable SI = (ItemSelectable)root;
	    if (caller instanceof ItemListener)
	        SI.removeItemListener ((ItemListener)caller);

	    List L = (List)root;
	    if (caller instanceof ActionListener)
	        L.removeActionListener ((ActionListener)caller);
	}

	else if (root instanceof ItemSelectable) {
	    ItemSelectable SI = (ItemSelectable)root;
	    if (caller instanceof ItemListener)
	        SI.removeItemListener ((ItemListener)caller);
	}

	else if (root instanceof MenuItem) {
	    MenuItem MI = (MenuItem)root;
	    if (caller instanceof ActionListener)
	        MI.removeActionListener ((ActionListener)caller);
	}

/* There should be a tagging interface for Action producers!
 * Buttons and things that act like Buttons.
 * Since there is not,
 * we find out by reflection,
 * if this class has a removeActionListener method. */

	else if (null != (removeActionListenerMethod
	 = getRemoveActionListenerMethod (root))) {
	    if (caller instanceof ActionListener) {
	        Object []params = { caller };
	        try { removeActionListenerMethod.invoke (root, params); }
		catch (Exception ex) {}
	    }
	}

	else if (root instanceof Adjustable) {
	    Adjustable A = (Adjustable)root;
	    if (caller instanceof AdjustmentListener)
	        A.removeAdjustmentListener ((AdjustmentListener)caller);
	}

	else if (root instanceof Container) {
	    Container C = (Container)root;
	    for (int ii = 0; ii < C.getComponentCount (); ii++)
		remove_Listeners (caller, C.getComponent (ii));
	}
    }

    private static java.lang.reflect.Method addActionListenerMethod = null;

    private static java.lang.reflect.Method
      getAddActionListenerMethod (Object cc) {

        Class []formals = { ActionListener.class };

	try { return cc.getClass ().getMethod ("addActionListener", formals); }
        catch (Exception ex) { return null; }
    }

    private static java.lang.reflect.Method removeActionListenerMethod = null;

    private static java.lang.reflect.Method
      getRemoveActionListenerMethod (Object cc) {

        Class []formals = { ActionListener.class };

	try { return cc.getClass ().getMethod ("removeActionListener", formals); }
        catch (Exception ex) { return null; }
    }

}

/** WindowEventForwarder implements WindowListener,
 * to listen for all WindowEvents on a given Window,
 * actually on some extension object which is an instanceof Window.
 * It attempts to forward each method invocation to that Window,
 * which will succeed if the window extension has that method,
 * and otherwise will throw a method-not-found exception,
 * which is just ignored.
 * For example,
 * some dialog might have only this --
 * public void windowClosing (WindowEvent we) { dispose (); }
 *
 * Compiler will **NOT** simply try { ww.method (arg) } directly,
 * because they are not a declared Window.method,
 * so we get it for the actual class if possible,
 * and invoke it if so. */

class WindowEventForwarder
implements WindowListener {

    Window ww;
    Class wc;

    java.lang.reflect.Method
      wm_windowOpened = null,
      wm_windowClosing = null,
      wm_windowClosed = null,
      wm_windowIconified = null,
      wm_windowDeiconified = null,
      wm_windowActivated = null,
      wm_windowDeactivated = null;

/* Notice the ".class" needed here! */

    Class []formals = { WindowEvent.class };

    WindowEventForwarder (Window ww) {
	this.ww = ww;
	wc = ww.getClass ();

	try { wm_windowOpened = wc.getMethod ("windowOpened", formals); }
      catch (Exception ex) { }

	try { wm_windowClosing = wc.getMethod ("windowClosing", formals); }
      catch (Exception ex) { }

	try { wm_windowClosed = wc.getMethod ("windowClosed", formals); }
      catch (Exception ex) { }

	try { wm_windowIconified = wc.getMethod ("windowIconified", formals); }
      catch (Exception ex) { }

	try { wm_windowDeiconified = wc.getMethod ("windowDeiconified", formals); }
      catch (Exception ex) { }

	try { wm_windowActivated = wc.getMethod ("windowActivated", formals); }
      catch (Exception ex) { }

	try { wm_windowDeactivated = wc.getMethod ("windowDeactivated", formals); }
      catch (Exception ex) { }
    }

    public boolean isUseful () {
      return ((wm_windowOpened != null)
       || (wm_windowClosing != null)
       || (wm_windowClosed != null)
       || (wm_windowIconified != null)
       || (wm_windowDeiconified != null)
       || (wm_windowActivated != null)
       || (wm_windowDeactivated != null));
    }

    public void windowOpened (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowOpened.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

    public void windowClosing (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowClosing.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

    public void windowClosed (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowClosed.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

    public void windowIconified (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowIconified.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

    public void windowDeiconified (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowDeiconified.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

    public void windowActivated (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowActivated.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

    public void windowDeactivated (WindowEvent we) {
	try {
	    Object []params = { we };
	    wm_windowDeactivated.invoke (ww, params);
	}
	catch (Exception ex) { }
    }

}

/** FocusEventForwarder implements FocusListener,
 * to listen for all FocusEvents on children of a given Container,
 * actually some extension object which is an instanceof Container.
 * It attempts to forward each method invocation to that Container,
 * which will succeed if the extension has that method,
 * and otherwise will throw a method-not-found exception,
 * which is just ignored.
 *
 * Compiler will **NOT** simply try { cc.method (arg) } directly,
 * because they are not a declared Container.method,
 * so we get it for the actual class if possible,
 * and invoke it if so. */

class FocusEventForwarder
implements FocusListener {

    Container cc;
    Class wc;

    java.lang.reflect.Method
      cc_focusGained = null,
      cc_focusLost = null;

/* Notice the ".class" needed here! */

    Class []formals = { FocusEvent.class };

    FocusEventForwarder (Container cc) {
	this.cc = cc;
	wc = cc.getClass ();

	try { cc_focusGained = wc.getMethod ("focusGained", formals); }
      catch (Exception ex) { }

	try { cc_focusLost = wc.getMethod ("focusLost", formals); }
      catch (Exception ex) { }
    }

    public boolean isUseful () {
      return ((cc_focusGained != null)
       || (cc_focusLost != null));
    }

    public void focusGained (FocusEvent we) {
	try {
	    Object []params = { we };
	    cc_focusGained.invoke (cc, params);
	}
	catch (Exception ex) { }
    }

    public void focusLost (FocusEvent we) {
	try {
	    Object []params = { we };
	    cc_focusLost.invoke (cc, params);
	}
	catch (Exception ex) { }
    }

}
/* <IMG SRC="/cgi-bin/counter">*/
