import  java.awt.*;
import java.util.*;

/** CursorLabel associates a String with a Component,
 * which is one of the children of a CursorPanel.
 * CursorLabel is itself associated with a PanelCursor,
 * in the same CursorPanel,
 * so that the String may be shown centered at the intersection,
 * where the PanelCursor crosses the Component.
 *
 * A PanelCursor belonging to a CursorPanel
 * may have CursorLabels for any number of the Components
 * that are children of the CursorPanel.
 *
 * 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.
 *
 * Application code would not normally construct a CursorLabel,
 * but would use the PanelCursor.addCursorLabel () method,
 * to add a CursorLabel to an existing PanelCursor,
 * which returns the added CursorLabel.
 * <IMG SRC="/cgi-bin/counter">

 * @author Morris Hirsch,
 * IPD Newport RI
 */

public class CursorLabel {

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

    private Component aComponent;
    private CursorPanel myPanel;
    private String [] message = null;

/** @param aComponent -- a child of the CursorPanel.
 * @param aString -- the message,
 * shown over the intersection of this Component
 * and the PanelCursor.
 *
 * Application code would not normally construct a CursorLabel,
 * but would use the PanelCursor.addCursorLabel () method,
 * to add a CursorLabel to an existing PanelCursor,
 * which returns the added CursorLabel.  */

    public CursorLabel (CursorPanel myPanel, Component aComponent, String aString) {
	this.myPanel = myPanel;
	this.aComponent = aComponent;
	setString (aString);
    }

/** @return -- the base Component.
 * The message will be shown over the intersection of this Component
 * and the PanelCursor.
 */

    public Component getComponent () {
	return aComponent;
    }

/** Set (or clear) message to display in this area.
 * @param message -- the message,
 * may use comma or space or embedded "\n" newlines,
 * to delimit multiple lines.
 *
 * Caution! The number of multiple lines that can be shown
 * depends on the heights of the related Components.
 * Too many multiple lines will cause overlap. */

   public void setString (String message) {
       if ((null == message) || (0 == message.length ())) {
	   this.message = null;
	   return;
       }

       StringTokenizer spec = new StringTokenizer
	   (message, ", \n");

       this.message = new String [spec.countTokens ()];
       for (int nn = 0;
            0 < spec.countTokens ();
	    nn++)
	   this.message [nn] = spec.nextToken ();
   }

   Dimension getMessageSize (Graphics g) {
      if ((null == message) || (0 == message.length))
	 return new Dimension (0, 0);

     FontMetrics fm = g.getFontMetrics ();

     int ssheight = message.length * fm.getHeight ();

     int sswidth = 0;
     for (int nn = 0; nn < message.length; nn++) {
       int xxwidth = fm.stringWidth (message [nn]);
       if (sswidth < xxwidth)
	   sswidth = xxwidth;
     }

     return new Dimension (sswidth+4, ssheight+4);
   }

/* Get location relative to ancestral CursorPanel,
 * either immediate parent or further removed. */

     Point relativeLocation (Component aComponent) {
	 Component aParent = aComponent.getParent ();
         Point aLocation = aComponent.getLocation ();

	 while ((null != aParent)
	     && (aParent != myPanel)) {
             Point pLocation = aParent.getLocation ();

	     aLocation.x += pLocation.x;
	     aLocation.y += pLocation.y;

	     aParent = aParent.getParent ();
	 }

	 return aLocation;
     }

/** We CENTER each message on the intersection */

   public void paint (Graphics g, int x) {

     if (null == aComponent)
	 return;

      if ((null == message) || (0 == message.length))
	 return;

     Dimension MS = getMessageSize (g);
     int swidth = MS.width;
     int sheight = MS.height;

     FontMetrics fm = g.getFontMetrics ();
     int ssheight = fm.getHeight ();

     Dimension ACS = aComponent.getSize ();
     Point location = relativeLocation (aComponent);

     int left = x - (swidth/2);
     int top = location.y + (ACS.height/2) - (sheight/2);

     g.setColor (Color.white);
     g.fillRect (left, top, swidth+1, sheight+1);

     g.setColor (Color.black);

     for (int nn = 0; nn < message.length; nn++)
       g.drawString (message [nn],
           x - (fm.stringWidth (message [nn]) / 2),
           top += ssheight);
   }

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