
package uk.co.wingpath.gui;

import java.awt.*;
import javax.swing.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.event.Event;
import uk.co.wingpath.util.*;

/**
* This class provides an abstract implementation of the WComponent interface.
* <p>Default implementations of most of the <code>WComponent</code> methods
* are provided, but concrete sub-classes must provide implementations of
* the <code>getValue</code> and <code>setValue</code> methods.
*/

public abstract class WAbstractComponent<T>
    implements WComponent<T>
{
    private JComponent component;
    private JLabel label;
    protected final ValueEventSource listeners = new ValueEventSource ();
    private boolean enabled = true;
    private boolean editable = true;

    /**
    * Initializes the <code>WAbstractComponent</code> with the specified
    * component and label.
    * <p>Sub-class constructors must call this method. This has been made a
    * method rather a constructor as a convenience for sub-class constructors
    * (they may do some of their own initialization before calling this
    * method, whereas a constructor would have to be called before doing
    * anything else).
    * @param component the underlying JComponent.
    * @param label text for the assocatiated JLabel, or <code>null</code> if
    * there is no assocatiated label.
    */
    protected void initialize (JComponent component, String label)
    {
        Event.checkIsEventDispatchThread ();
        this.component = component;
        if (label == null)
        {
            this.label = null;
        }
        else
        {
            if (!label.equals (""))
                label += ":";
            this.label = new JLabel (label, SwingConstants.LEFT);
            this.label.setLabelFor (component);
        }
    }

    public JComponent getComponent ()
    {
        return component;
    }

    public JLabel getLabel ()
    {
        return label;
    }

    /**
    * Always returns <code>true</code>.
    * @return <code>true</code>.
    */
    public boolean checkValue ()
    {
        return true;
    }

    public boolean hasValueChanged (T oldValue)
    {
        boolean changed = false;
        if (oldValue == null)
        {
            changed = getValue () != null;
        }
        else
        {
            changed = !oldValue.equals (getValue ());
        }
        setBackground (changed ? Gui.COLOUR_BACKGROUND_UNAPPLIED :
            Gui.COLOUR_BACKGROUND_NORMAL);
        return changed;
    }

    public void setWidth (int width)
    {
        Event.checkIsEventDispatchThread ();
        Dimension prefSize = component.getPreferredSize ();
        if (prefSize.width < width)
            component.setPreferredSize (new Dimension (width, prefSize.height));
        Dimension minSize = component.getMinimumSize ();
        if (minSize.width < width)
            component.setMinimumSize (new Dimension (width, minSize.height));
        component.setMaximumSize (component.getPreferredSize ());
    }

    public void setWidthChars (int width)
    {
        Event.checkIsEventDispatchThread ();
        setWidth (Gui.getTextWidth (width));
    }

    /**
    * Does nothing.
    * @param alignment the alignment
    */
    public void setAlignment (int alignment)
    {
    }

    public void setEnabled (boolean enabled)
    {
        Event.checkIsEventDispatchThread ();
        this.enabled = enabled;
        component.setEnabled (enabled && editable);
        if (label != null)
            label.setEnabled (enabled);
    }

    /**
    * Calls the <code>setEnabled</code> method.
    * @param editable whether the component should be editable.
    */
    public void setEditable (boolean editable)
    {
        this.editable = editable;
        component.setEnabled (enabled && editable);
        component.setFocusable (editable);
    }

    public boolean isEditable ()
    {
        return editable;
    }

    public void setToolTipText (String text)
    {
        Event.checkIsEventDispatchThread ();
        component.setToolTipText (text);
        if (label != null)
            label.setToolTipText (text);
    }

    /**
    * Does nothing.
    * @param text tooltip text array.
    */
    public void setToolTipText (String [] text)
    {
    }

    public void setMnemonic (int mnemonic)
    {
        if (label != null)
            label.setDisplayedMnemonic (mnemonic);
    }

    public void setMnemonic (int mnemonic, int index)
    {
        Event.checkIsEventDispatchThread ();
        if (label != null)
        {
            label.setDisplayedMnemonic (mnemonic);
            label.setDisplayedMnemonicIndex (index);
        }
    }

    public void setMirror (MirrorField mirror)
    {
    }

    public void requestFocusInWindow ()
    {
        Event.checkIsEventDispatchThread ();
        component.requestFocusInWindow ();
    }

    public void setBackground (Color bg)
    {
        component.setBackground (bg);
    }

    public void addValueListener (ValueListener l)
    {
        listeners.addListener (l);
    }

    public void removeValueListener (ValueListener l)
    {
        listeners.removeListener (l);
    }

    /**
    * Notifies all listeners that the value of the component has changed
    * or is changing.
    * @param changing whether the user is editing the value.
    */
    protected void fireValueChanged (boolean changing)
    {
        listeners.fireValueChanged (this, changing);
    }
}

