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

/** * PanelCursor implements Adjustable,
 * reports AdjustmentEvents under the new Event model,
 * with addAdjustmentListener (AdjustmentListener).

 * Caller may do this to be a Listener on PanelCursor myAdj --
 * Caller ... implements AdjustmentListener ...
 * public void adjustmentValueChanged (AdjustmentEvent ae) { ... }
 * myAdj.addAdjustmentListener (this);

 * This extension adds variables and methods.
 * BEWARE our boolean Visible and their int VisibleAmount,

 * Adjustable parameters and returns are all integers,
 * the VisibleAmount is the width of the Cursor,
 * the Value is the left edge of the Cursor,

 * Value is constrained by Minimum/Maximum,
 * here tied to a "base" child of the Panel,
 * or the CursorPanel itself.

 * Application code would not normally construct a PanelCursor,
 * but would use CursorPanel.addCursor () which returns the
 * added PanelCursor.
 * @author Morris Hirsch IPD Inc 1998
 */

public class PanelCursor extends Object implements Adjustable {

  public String getVersion () { return "$Id: PanelCursor.java,v 1.3 1998/09/03 13:00:24 mhirsch Exp $"; }

/* Variables and methods required by Adjustable */

    private int
      aMinimum,
      aMaximum,
      aUnitIncrement = 1,
      aBlockIncrement = 1,
      aValue,
      aVisibleAmount;

    public void setMinimum (int aMinimum) {
	this.aMinimum = aMinimum;
	enforce_limits ();
    }

    public int getMinimum () {
	return aMinimum;
    }

    public void setMaximum (int aMaximum) {
	this.aMaximum = aMaximum;
	enforce_limits ();
    }

    public int getMaximum () {
	return aMaximum;
    }

/** No use for UnitIncrement or BlockIncrement at this time. */

    public void setUnitIncrement (int aUnitIncrement) {
	this.aUnitIncrement = aUnitIncrement;
    }

    public int getUnitIncrement () {
	return aUnitIncrement;
    }

    public void setBlockIncrement (int aBlockIncrement) {
	this.aBlockIncrement = aBlockIncrement;
    }

    public int getBlockIncrement () {
	return aBlockIncrement;
    }

/** Set cursor value,
 * subject to establish min and max limits.
 * Fire an event if any listeners. */

    public void setValue (int aValue) {
	int oldValue = this.aValue;
	this.aValue = aValue;
	enforce_limits ();

/** Fire an Adjustment if wanted...
 * Source is the Dragged Cursor.  */

       if ((oldValue != this.aValue)
	&& (null != adjl))
	     adjl.adjustmentValueChanged (new AdjustmentEvent
	       (this,
                AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
                AdjustmentEvent.TRACK,
		getValue ()));
    }

/** Get current pixel value,
 * relative to the base Component if specified,
 * otherwise directly in the CursorPanel. */

    public int getValue () {
	return aValue;
    }

/** Set / Get cursor width in pixels */

    public void setVisibleAmount (int aVisibleAmount) {
	this.aVisibleAmount = aVisibleAmount;
	enforce_limits ();
	if (aVisible)
	    parent.repaint (100);
    }

    public int getVisibleAmount () {
	return aVisibleAmount;
    }

/** Only HORIZONTAL motion for now -- could have both */

    public int getOrientation () { return Adjustable.HORIZONTAL; }

    private AdjustmentListener adjl = null;

/** Caller may add / remove an AdjustmentListener,
 * Source is this dragged cursor.
 * The event provides only the integer value,
 * but caller may use the Source to get the min and max,
 * or better,
 * to get the fraction. */

    public void addAdjustmentListener (AdjustmentListener al) {
	this.adjl = AWTEventMulticaster.add (this.adjl, al);
    }

    public void removeAdjustmentListener (AdjustmentListener al) {
	this.adjl = AWTEventMulticaster.remove (this.adjl, al);
    }

/* Ensure full width of the cursor within min and max limits */

    void enforce_limits () {
	if (aValue < aMinimum)
	    aValue = aMinimum;

        if (aValue + aVisibleAmount > aMaximum)
	    aValue = aMaximum - aVisibleAmount;

	aFraction = (float)(aValue - aMinimum)
	    / (float)(aMaximum - aMinimum - aVisibleAmount);
    }

/* Variables and methods for PanelCursor extension */

    private CursorPanel parent;
    private Component base;
    private float aFraction;
    private Color aColor;
    private boolean aDraggable;
    private boolean aVisible;
    private int aStyle;

/**
 * Although each PanelCursor carries values for drawing parameters,
 * and this class has logic implementing the actual drawing,
 * the drawing is done in the containing CursorPanel,
 * using passed Graphics context of the CursorPanel.
 *
 * @param base -- some child of the CursorPanel,
 * if null we use the CursorPanel itself,
 * used to establish range of the Cursor.
 * @param aFraction -- initial value as Fraction of range.
 * @param aVisibleAmount -- pixels width of Cursor.
 * @param aColor -- Color of Cursor.
 * @param aStyle -- drawing Style for this Cursor.
 * @param aDraggable -- true if Cursor may be dragged by user.
 *
 * Application code would not normally construct a PanelCursor,
 * but would use CursorPanel.addCursor () which returns the
 * added PanelCursor. */

   public PanelCursor (CursorPanel parent, Component base, float aFraction, int aVisibleAmount, Color aColor, int aStyle, boolean aDraggable) {

	this.parent = parent;

	setBase (base);

	this.aVisibleAmount = aVisibleAmount;

        setFraction (aFraction);

	this.aColor = aColor;
	this.aStyle = aStyle;
	this.aDraggable = aDraggable;
	this.aVisible = true;

        parent.repaint (100);
    }

/** Set the Color
 * @param aColor -- Color of Cursor.
 */

    public void setColor (Color aColor) {
        this.aColor = aColor;
	if (aVisible)
	    parent.repaint (100);
    }

/** Get the Color
 * @return
 */

    public Color getColor () {
        return aColor;
    }

/** Set the Style
 * @param aStyle -- drawing Style for this Cursor.
 */

    public void setStyle (int aStyle) {
        this.aStyle = aStyle;
	if (aVisible)
	    parent.repaint (100);
    }

/** Get the Style
 * @return
 */

    public int getStyle () {
        return aStyle;
    }

/** Set the position as a Fraction
 * @param aFraction -- initial value as Fraction of range.
 */

    public void setFraction (float aFraction) {
        this.aFraction = aFraction;
	aValue = aMinimum
	 + (int)(aFraction * (aMaximum - aMinimum - aVisibleAmount));
        enforce_limits ();
	if (aVisible)
	    parent.repaint (100);
    }

/** Get the position as a Fraction
 * @return
 */

    public float getFraction () {
        return aFraction;
    }

/** Set the Draggable flag
 * @param aDraggable -- true if Cursor may be dragged by user.
 */

    public void setDraggable (boolean aDraggable) {
        this.aDraggable = aDraggable;
    }

/** Get the Draggable flag
 * @return
 */

    public boolean getDraggable () {
        return aDraggable;
    }

/** Set the Visible flag
 * @param aVisible -- true if Cursor is visible.
 */

    public void setVisible (boolean aVisible) {
        this.aVisible = aVisible;
	if (aVisible)
	    parent.repaint (100);
    }

/** Get the Visible flag
 * @return true if Cursor is visible.
 */

    public boolean getVisible () {
        return aVisible;
    }

/** Set the base reference for cursor range
 * @param base -- some child of the CursorPanel,
 * if null use the CursorPanel directly.  */

    public void setBase (Component base) {
	this.base = base;
	invalidate ();
    }

/** Recompute for changed base or base base size */

    public void invalidate () {

        if (null != base) {
	    this.aMinimum = base.getLocation ().x;
	    this.aMaximum = aMinimum + base.getSize ().width;
	}
	else {
	    this.aMinimum = 0;
	    this.aMaximum = parent.getSize ().width;
	}

	aValue = aMinimum
	 + (int)(aFraction * (aMaximum - aMinimum - aVisibleAmount));
    }

/** Paint in the CursorPanel using passed Graphics and Dimension */

    public void paint (Graphics g, Dimension d) {
	if (aVisible) {
	    g.setColor (aColor);
	    int cw = aVisibleAmount;

/* "Dotted" */

	    if (0 < aStyle)
		for (int hh = 0; hh < d.height; hh += cw) {
		    g.fillRect
			(aValue, hh,
			 cw, cw);
                    hh += (aStyle * cw);
		}

/* "Dashed" */

	    else if (0 > aStyle)
		for (int hh = 0; hh < d.height; hh += cw) {
		    g.fillRect
			(aValue, hh,
			 cw, -(aStyle * cw));
                    hh -= (aStyle * cw);
		}

/* "Thin" solid */

            else if (cw < 12)
		g.fillRect
		    (aValue, 5,
			cw, d.height-10);

/* "Thick" hollow */

            else
		g.drawRect
		    (aValue, 5,
			cw, d.height-10);

/* Paint any CursorLabels associated with this PanelCursor,
 * Cursor aValue provides the X coordinate,
 * each intersected Component provides a Y coordinate */

        if (null != CursorLabels)
            for (int ii = 0; ii < CursorLabels.length; ii++)
		if (null != CursorLabels[ii])
		    CursorLabels[ii].paint (g, aValue);

	}
    }

/* Moving Labels associated with this Cursor.
 * Maybe this should be a Hashtable --
 * some things get easier but some get harder. */

    private CursorLabel [] CursorLabels = null;

    public CursorLabel [] getCursorLabels () {
	return CursorLabels;
    }

/** Add or update a CursorLabel on this Cursor and return a handle,
 * schedule a parent.repaint to make sure it is shown.
 *
 * @param subject -- The child of Panel that CursorLabel travel must cover,
 * @param aString -- The message.
 *
 * A PanelCursor may have more than one CursorLabel
 * but not more than one defined for a given Component,
 * as they would be shown in the same place.  */

    public CursorLabel addCursorLabel (Component subject, String aString) {

        if (null == CursorLabels) {
            CursorLabels = new CursorLabel[15];
        }

/* Find existing CursorLabel with this Component subject,
 * update message to aString. */

        for (int ii = 0; ii < CursorLabels.length; ii++)
            if ((null != CursorLabels[ii])
             && (subject == CursorLabels[ii].getComponent ())) {
                CursorLabels[ii].setString (aString);
 // System.out.println ("Update "+ii+CursorLabels[ii]+" "+subject+" "+aString);
                parent.repaint (100);
	        return CursorLabels[ii];
            }

/* Construct and add new CursorLabel with this Component subject,
 * and message aString. */

        for (int ii = 0; ii < CursorLabels.length; ii++)
            if (null == CursorLabels[ii]) {
                CursorLabels[ii] = new CursorLabel (parent, subject, aString);
 // System.out.println ("Construct "+ii+CursorLabels[ii]+" "+subject+" "+aString);
	        parent.repaint (100);
                return CursorLabels[ii];
            }

/* No room,
 * this would not happen with a Hashtable,
 * but how many do we really need? */

        return null;
    }

/** Remove all CursorLabels from this Cursor */

    public void removeAllCursorLabels () {
        CursorLabels = null;
        parent.repaint (100);
    }

/** Remove a CursorLabel by handle from this Cursor */

    public void removeCursorLabel (CursorLabel aCursorLabel) {
        if (null != CursorLabels)
            for (int ii = 0; ii < CursorLabels.length; ii++)
                if (aCursorLabel == CursorLabels[ii]) {
                    CursorLabels[ii] = null;
		    return;
                }
    }

/** Remove a CursorLabel by subject Component from this Cursor */

    public void removeCursorLabel (Component subject) {
        if (null != CursorLabels)
            for (int ii = 0; ii < CursorLabels.length; ii++)
                if ((null != CursorLabels[ii])
                 && (subject == CursorLabels[ii].getComponent ())) {
                    CursorLabels[ii] = null;
		    return;
                }
    }

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