package net.xoetrope.awt;
import java.io.Reader;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import java.util.Hashtable;
/**
* <p>A widget for displaying an image and associating hotspots at
* coordinates specified in an external file</p>
* <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003<br>
* License: see license.txt
* @version $Revision: 2.3 $
*/
public class XHotspotImage extends XImage implements MouseMotionListener
{
/**
* The hand cursor - the normal pointer for a hotspot
*/
protected static Cursor handCursor;
/**
* The default mouse over cursor - the arrow
*/
protected static Cursor defaultCursor;
/**
* The hotspots
*/
protected Vector hotspots;
/**
* The images
*/
protected Vector images;
/**
* The hotspot names
*/
protected Vector names;
/**
* The disabled hotspots
*/
protected Hashtable disabled;
/**
* Constructor. Sets cursors and creates the Vector which stores the hotspots.
* Adds a new MouseMotionListener to the image
*/
public XHotspotImage()
{
if ( handCursor == null ) {
handCursor = Cursor.getPredefinedCursor( Cursor.HAND_CURSOR );
defaultCursor = Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR );
}
hotspots = new Vector();
images = new Vector();
addMouseMotionListener( this );
}
/**
* Move the hotspot from the disabled Hashtable to the main Vector
* @param name the name of the hotspot to enable
*/
public void enableHotspot( String name )
{
if ( disabled != null ){
Object o = disabled.remove( name );
if ( o != null ) {
names.addElement( name );
hotspots.addElement( o );
hotspotEnabled( name );
}
}
}
/**
* Convenience method to inform subclasses when a hotspot has been enabled
* @param name the name of the hotspot being enabled.
*/
protected void hotspotEnabled( String name ) {}
/**
* Create a hashtable to store the disabled hotspots and move the disabled
* one into it from the main Vector.
* @param name the name of the hotspot to be disabled
*/
public void disableHotspot( String name )
{
if ( disabled ==null )
disabled = new Hashtable();
for ( int i = 0; i < names.size(); i++ ) {
String temp = ( String )names.elementAt( i );
if ( temp.compareTo( name )==0 ) {
Object key = names.elementAt( i );
Object hotspot = hotspots.elementAt( i );
hotspots.removeElementAt( i );
names.removeElementAt( i );
disabled.put( key, hotspot );
hotspotDisabled( name, i );
}
}
}
/**
* Convenience method to inform subclasses when a hotspot has been disabled
* @param name the name of the hotspot being disabled.
* @param idx the index of the hotspot being disabled
*/
protected void hotspotDisabled( String name, int idx ) {}
/**
* Get the name of the hotspot in element i of the names vector
* @param i The index of the hotspot
* @return The Name of the hotspot
*/
public String getName( int i )
{
if ( i != -1 )
return ( String )names.elementAt( i );
return null;
}
/**
* Reads the hotspot information from the reader parameter and adds them to
* the names and hotspots vectors.
* @param r Reader of the file.
*/
public void read( Reader r )
{
names = new Vector();
hotspots = new Vector();
XmlElement element = XmlSource.read( r );
if (( element == null ) || ( element.getChildren() == null ))
return;
Enumeration e = element.getChildren().elements();
while ( e.hasMoreElements() ) {
XmlElement ele = ( XmlElement )e.nextElement();
Object name = ele.getAttribute( "name" );
Object image = ele.getAttribute( "image" );
if ( name == null )
names.addElement( "" );
else
names.addElement( ele.getAttribute( "name" ).toString() );
addHotspot( ele.getAttribute( "coords" ).toString(), image == null ? "" : image.toString() );
handleElement( ele );
}
}
/**
* Not implemented
* @param ele an XML element
*/
protected void handleElement( XmlElement ele )
{
}
private int getNextTokenAsInt( StringTokenizer tokenizer )
{
String s = tokenizer.nextToken().trim();
return Integer.parseInt( s );
}
/**
* Creates a polygon and iterates the coordinates in the pts paramater adding
* each to the polygon. Adds the polygon to the hotspots Vector
* @param pts String containing the points pairs of the polygon.
*/
private void addHotspot( String pts, String image )
{
Polygon p = new Polygon();
StringTokenizer tokenizer = new StringTokenizer( pts, "," );
int x0 = getNextTokenAsInt( tokenizer );
int y0 = getNextTokenAsInt( tokenizer );
p.addPoint( x0, y0 );
int x, y;
while ( tokenizer.hasMoreTokens() ) {
x = getNextTokenAsInt( tokenizer );
y = getNextTokenAsInt( tokenizer );
p.addPoint( x, y );
}
// Close the polygon
p.addPoint( x0, y0 );
hotspots.addElement( p );
images.addElement( image );
}
/**
* Check the Point p to see if any of the polygons in the hotspots Vector contain
* it. If so return the index of the Vector element.
* @param p The Point which we are checking
* @return The index of the polygon which contains the point or -1 if none of
* the polygons contain the Point
*/
public int checkHotspot( Point p )
{
int numPolygons = hotspots.size();
for ( int i = 0; i < numPolygons; i++ ) {
Polygon poly = ( Polygon )hotspots.elementAt( i );
if ( poly.contains( p ) ) {
setCursor( handCursor );
return i;
}
}
setCursor( defaultCursor );
return -1;
}
/**
* Call the checkHotSpot function to change the cursor
* @param e MouseEvent
*/
public void mouseMoved( MouseEvent e )
{
checkHotspot( e.getPoint() );
}
/**
* Unused method.
* @param e the mouse event
*/
public void mouseDragged( MouseEvent e )
{
}
}