/*
 * @(#)JPTComponent.java    1.0  15 December 2003
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import javax.swing.*;
import java.beans.*;

/**
 * <P>The abstract class <CODE>JPTComponent</CODE> extends <CODE>JComponent</CODE>
 * in order to support property change and add mouse and key adapters.</P>
 *
 * <P>Specifically, this class:</P>
 *
 * <UL>
 *   <LI>implements those methods in <CODE>SupportsPropertyChange</CODE> that are
 *       not already (publically) implemented in <CODE>JComponent</CODE></LI>
 *   <LI>adds a <CODE>KeyActionAdapter</CODE> for this component</LI>
 *   <LI>adds a <CODE>MouseActionAdapter</CODE> for this component</LI>
 * </UL>
 *
 * @author  Richard Rasala
 * @version 2.3
 * @since   2.3
 */
public abstract class JPTComponent
    extends JComponent
    implements SupportsPropertyChange
{
    /**
     * <P>The forwarding listener for this JPTComponent object to implement
     * the interface <CODE>SupportsPropertyChange</CODE>.</P>
     *
     * @see #getForwardingListener()
     */
    private PropertyChangeForwardingListener forwardingListener
        = new PropertyChangeForwardingListener(this);
    
    
    /** The key action adapter. */
    private KeyActionAdapter keyActionAdapter = new KeyActionAdapter(this);
    
    
    /** The mouse action adapter. */
    private MouseActionAdapter mouseActionAdapter = new MouseActionAdapter(this);
    
    
    /**
     * Add all items in the given <CODE>PropertyChangeListener</CODE> array
     * to the listener list.  These items are registered for all properties.
     *
     * @param listeners the PropertyChangeListener array to be added
     */
    public final void addPropertyChangeListeners(PropertyChangeListener[] listeners)
    {
        if (listeners == null)
            return;
        
        int length = listeners.length;
        
        for (int i = 0; i < length; i++)
            addPropertyChangeListener(listeners[i]);
    }
    
    
    /**
     * Add all items in the given <CODE>PropertyChangeListener</CODE> array to
     * the listener list for a specific property.  These items will be invoked
     * only when a call on <CODE>firePropertyChange</CODE> names that specific
     * property.
     *
     * @param listeners the PropertyChangeListener array to be added
     */
    public final void addPropertyChangeListeners(
        String propertyName,
        PropertyChangeListener[] listeners)
    {
        if (listeners == null)
            return;
        
        int length = listeners.length;
        
        for (int i = 0; i < length; i++)
            addPropertyChangeListener(propertyName, listeners[i]);
    }
    
    
    /**
     * Check if there are any listeners for a specific property.
     *
     * @param propertyName the name of the property to check 
     */
    public final boolean hasListeners(String propertyName) {
        PropertyChangeListener[] listeners
            = getPropertyChangeListeners(propertyName);
        
        return (listeners == null) ? false : (listeners.length > 0);
    }
    
    
    /**
     * <P>Report a bound property update to any registered listeners.</P>
     *
     * <P>No event is fired if the old and new values are equal and
     * non-<CODE>null</CODE>.</P>
     *
     * <P>In order to implement <CODE>SupportsPropertyChange</CODE>, this
     * method makes <CODE>public</CODE> a method that is declared
     * <CODE>protected</CODE> in <CODE>JComponent</CODE>.</P>
     *
     * @param propertyName the programmatic name of the property that was changed
     * @param oldValue the old value of the property
     * @param newValue the new value of the property
     */
    public final void firePropertyChange(
        String propertyName,
        Object oldValue,
        Object newValue)
    {
        super.firePropertyChange(propertyName, oldValue, newValue);
    }
    
    
    /**
     * <P>Fire an existing <CODE>PropertyChangeEvent</CODE> to any registered
     * listeners.</P>
     
     * <P>No event is fired if the given event's old and new values are equal
     * and non-<CODE>null</CODE>.</P>
     *
     * @param evt the PropertyChangeEvent object
     */
    public final void firePropertyChange(PropertyChangeEvent evt) {
        if (evt == null)
            return;
        
        String property = evt.getPropertyName();
        Object oldValue = evt.getOldValue();
        Object newValue = evt.getNewValue();
        
        firePropertyChange(property, oldValue, newValue);
    }
    
    
    /**
     * Returns the <CODE>PropertyChangeForwardingListener</CODE> that
     * will forward the property change events it receives to this object.
     *
     * @return the forwarding listener
     */
    public final PropertyChangeForwardingListener getForwardingListener() {
        return forwardingListener;
    }
    
    

    /**
     * Add the forwarding listener as a property change listener
     * for the given object if the object supports property change.
     *
     * @param object the object that should add the forwarding listener
     */
    public final void addForwardingListener(Object object) {
        if (object instanceof SupportsPropertyChange) {
            SupportsPropertyChange spc = (SupportsPropertyChange) object;
            spc.addPropertyChangeListener(getForwardingListener());
        }
    }
    
    
    /**
     * Remove the forwarding listener as a property change listener
     * for the given object if the object supports property change.
     *
     * @param object the object that should remove the forwarding listener
     */
    public final void removeForwardingListener(Object object) {
        if (object instanceof SupportsPropertyChange) {
            SupportsPropertyChange spc = (SupportsPropertyChange) object;
            spc.removePropertyChangeListener(getForwardingListener());
        }
    }
    
    
    /**
     * Remove the forwarding listener as a property change listener
     * for the old object if the old object supports property change
     * and add the forwarding listener as a property change listener
     * for the new object if the new object supports property change.
     *
     * @param oldobject the old object that should remove the forwarding listener
     * @param newobject the new object that should add the forwarding listener
     */
    public final void removeAndAddForwardingListener(Object oldobject, Object newobject)
    {
        removeForwardingListener(oldobject);
        addForwardingListener(newobject);
    }    
    
    
    /**
     * Returns this component's key action adapter.
     *
     * @return this component's key action adapter
     */
    public final KeyActionAdapter getKeyActionAdapter() {
        return keyActionAdapter;
    }
    
    
    /**
     * Returns this component's mouse action adapter.
     *
     * @return this component's mouse action adapter
     */
    public final MouseActionAdapter getMouseActionAdapter() {
        return mouseActionAdapter;
    }
    
    
    /** Refreshes the component by repacking the parent window. */
    public void refreshComponent() {
        Refresh.packParentWindow(this);
    }
    
}
