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

/** CheckImageButton extends ImageButton,
 * with a StickyButtonLikeListener,
 * providing Checkbox functionality.
 * <IMG SRC="/cgi-bin/counter">
 * @author  David Geary
 */

public class CheckImageButton extends ImageButton
implements ItemSelectable, Runnable {

    private StickyButtonLikeListener SIBL = null;
    private RadioGroup RG = null;

    public String getVersion () { 
        return "$Id: CheckImageButton.java,v 1.4 1998/07/27 20:26:13 mhirsch Exp $"; 
    }

    public String toString () {
	return super.toString ()+",state="+getState ();
    }

/** --- Constructors that take an Image --- */

/** @param image -- the image to show.
 * @param RG -- the RadioGroup to manage this control.
 * @param text -- the text to show, or null.
 * @param state -- initial state of the button.
 */

    CheckImageButton (Image image, RadioGroup RG, String text, boolean state) {
        super (new StickyButtonLikeListener (), image, 2, text);
        SIBL = (StickyButtonLikeListener) getListener ();
	this.RG = RG;
        setState (state);
    }

/** @param image -- the image to show.
 * @param RG -- the RadioGroup to manage this control.
 * @param state -- initial state of the button.
 */

    CheckImageButton (Image image, RadioGroup RG, boolean state) {
        this (image, RG, null, state);
    }

/** @param image -- the image to show.
 * @param RG -- the RadioGroup to manage this control.
 */

    CheckImageButton (Image image, RadioGroup RG) {
        this (image, RG, null, false);
    }

/** @param image -- the image to show.
 * @param state -- initial state of the button.
 */

    CheckImageButton (Image image, boolean state) {
	this (image, null, null, state);
    }

/** @param image -- the image to show.
 */

    CheckImageButton (Image image) {
        this (image, null, null, false);
    }

/** --- Constructors that take a DrawImage --- */

/** @param drawImage -- the image to show.
 * @param text -- the text to show, or null.
 * @param state -- initial state of the button.
 */

    CheckImageButton (DrawImage drawImage, RadioGroup RG, String text, boolean state) {
        super (new StickyButtonLikeListener (), drawImage, 2, text);
        SIBL = (StickyButtonLikeListener) getListener ();
	this.RG = RG;
        setState (state);
    }

/** @param drawImage -- the image to show.
 * @param RG -- the RadioGroup to manage this control.
 * @param state -- initial state of the button.
 */

    CheckImageButton (DrawImage drawImage, RadioGroup RG, boolean state) {
        this (drawImage, RG, null, state);
    }

/** @param drawImage -- the image to show.
 * @param RG -- the RadioGroup to manage this control.
 */

    CheckImageButton (DrawImage drawImage, RadioGroup RG) {
        this (drawImage, RG, false);
    }

/** @param drawImage -- the image to show.
 * @param state -- initial state of the button.
 */

    CheckImageButton (DrawImage drawImage, boolean state) {
        this (drawImage, null, state);
    }

/** @param drawImage -- the image to show.
 */

    CheckImageButton (DrawImage drawImage) {
        this (drawImage, null, null, false);
    }

/* --- Public Methods --- */

/** Set the state,
 * have RadioGroup (if any) turn off any prior selection,
 * update the "state machine" in the event handler,
 * also update the appearance of the button.
 *
 * Here for internal call,
 * if this is the RG current selection,
 * and it is being turned off,
 * let it happen,
 * and null out the RG current selection.
 */

    public void setState (boolean state) {
        SIBL.setState (state);

	if (null != RG) {
	    if (state)
	        RG.setSelected (this);
	    else if (this == RG.getSelected ())
	        RG.setSelected (null);
	}

        setPressed (state);
    }

/** Get the current state */

    public boolean getState () { return SIBL.getState (); }

/** Set the RadioGroup.
 * @param RG -- the RadioGroup to manage this control.
 * May also be done via the constructor. */

    public void setRadioGroup (RadioGroup RG) {
	this.RG = RG;
    }

    private ItemListener itemL = null;

/** Caller may add / remove an ItemListener,
 * same functionality as Checkbox.
 */

    public void addItemListener (ItemListener al) {
        this.itemL = AWTEventMulticaster.add (this.itemL, al);
    }

    public void removeItemListener (ItemListener al) {
        this.itemL = AWTEventMulticaster.remove (this.itemL, al);
    }

/** @return -- array of (just one) SelectedObjects,
 * uses array although only one may be selected here.
 * The same situation as a single Checkbox.
 */

    public Object [] getSelectedObjects () {
        if (SIBL.getState () && (null != getText ())) {
            Object [] SO = new Object [1];
            SO [0] = getText ();
            return SO;
        }

        return null;
    }

/** Fire an ItemEvent if wanted,
 * either SELECTED or DESELECTED,
 * source is this button..
 *
 * Here for user event response,
 * if this is the RG current selection,
 * and it is being turned off,
 * do *NOT* let it happen,
 * turn ourself back on,
 * remain as the RG current selection,
 * and do not notify listeners.
 *
 * We **MUST** use a separate Thread to notify listeners,
 * so we and our calling event handler can immediately return.
 * Otherwise,
 * if the event listener happens to launch a modal dialog,
 * we don't wrap up for a long time,
 * and the event will be gone by the time we return,
 * causing the event distributor to throw a NullPointerException!
 */

    public void processStateChange (boolean state) {
	if (null != RG) {
	    if (state)
	        RG.setSelected (this);
	    else if (this == RG.getSelected ()) {
		setState (true);
		return;
	    }
	}

        if (null != itemL)
	    new Thread (this).start ();
    }

    public void run () {

        try {
            itemL.itemStateChanged (new ItemEvent
              (this,
              ItemEvent.ITEM_STATE_CHANGED,
              this.getText (),
              (this.getState ()) ? ItemEvent.SELECTED : ItemEvent.DESELECTED));
        }
        catch (Exception exc)
        { System.out.println ("failed processStateChange:"+exc);
          exc.printStackTrace (); }
    }
}
/* <IMG SRC="/cgi-bin/counter">*/
