// This example is from the book "Java in a Nutshell, Second Edition".
// Written by David Flanagan.  Copyright (c) 1997 O'Reilly & Associates.
// You may distribute this source code for non-commercial purposes only.
// You may study, modify, and use this example for any purpose, as long as
// this notice is retained.  Note that this example is provided "as is",
// WITHOUT WARRANTY of any kind either expressed or implied.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;

/**
 * A Java applet that simulates a client-side imagemap.
 * Plays a sound whenever the user clicks on one of the hyperlinks.
 */
public class Soundmap extends Applet {
  protected Image image;      // The image to display.
  protected Vector rects;     // A list of rectangles in it.
  protected AudioClip sound;  // A sound to play on user clicks in a rectangle.

  /** Initialize the applet */
  public void init() {
    // Look up the name of the image, relative to a base URL, and load it.
    // Note the use of three Applet methods in this one line.
    image = this.getImage(this.getDocumentBase(), this.getParameter("image"));

    // Lookup and parse a list of rectangular areas and the URLs they map to.
    // The convenience routine getRectangleParameter() is defined below.
    rects = new Vector();
    ImagemapRectangle r;
    for(int i = 0; (r = getRectangleParameter("rect" + i)) != null; i++)
      rects.addElement(r);

    // Look up a sound to play when the user clicks one of those areas.
    sound = this.getAudioClip(this.getDocumentBase(),
			      this.getParameter("sound"));

    // Specify an "event listener" object to respond to mouse button
    // presses and releases.  Note that this is the Java 1.1 event model.
    // Note that it also uses a Java 1.1 inner class, defined below.
    this.addMouseListener(new Listener());
  }

  /** Called when the applet is being unloaded from the system.
   * We use it here to "flush" the image we no longer need. This may
   * result in memory and other resources being freed more quickly. */
  public void destroy() { image.flush(); }

  /** To display the applet, we simply draw the image. */
  public void paint(Graphics g) { g.drawImage(image, 0, 0, this); }

  /** We override this method so that it doesn't clear the background
   * before calling paint().  No clear is necessary, since paint() overwrites
   * everything with an image.  Causes less flickering this way. */
  public void update(Graphics g) { paint(g); }

  /** Parse a comma-separated list of rectangle coordinates and a URL.
   * Used to read the imagemap rectangle definitions from applet parameters */
  protected ImagemapRectangle getRectangleParameter(String name) {
    int x, y, w, h;
    URL url;
    String value = this.getParameter(name);
    if (value == null) return null;

    try {
      StringTokenizer st = new StringTokenizer(value, ",");
      x = Integer.parseInt(st.nextToken());
      y = Integer.parseInt(st.nextToken());
      w = Integer.parseInt(st.nextToken());
      h = Integer.parseInt(st.nextToken());
      url = new URL(this.getDocumentBase(), st.nextToken());
    }
    catch (NoSuchElementException e) { return null; }
    catch (NumberFormatException e) { return null; }
    catch (MalformedURLException e) { return null; }

    return new ImagemapRectangle(x, y, w, h, url);
  }

  /**
   * An instance of this inner class is used to respond to mouse events
   */
  class Listener extends MouseAdapter {
    /** The rectangle that the mouse was pressed in. */
    private ImagemapRectangle lastrect;

    /** Called when a mouse button is pressed. */
    public void mousePressed(MouseEvent e) {
      // On button down, check if we're inside one of the specified rectangles.
      // If so, highlight the rectangle, display a message, and play a sound.
      // The utility routine findrect() is defined below.
      ImagemapRectangle r = findrect(e);
      if (r == null) return;
      Graphics g = Applet.this.getGraphics();
      g.setXORMode(Color.red);
      g.drawRect(r.x, r.y, r.width, r.height);  // highlight rectangle
      Applet.this.showStatus("To: " + r.url);   // display URL
      sound.play();                             // play the sound
      lastrect = r;   // Remember the rectangle so it can be un-highlighted.
    }

    /** Called when a mouse button is released. */
    public void mouseReleased(MouseEvent e) {
      // When the button is released, unhighlight the rectangle.  If the
      // mouse is still inside it, ask the browser to go to the URL.
      if (lastrect != null) {
	Graphics g = Applet.this.getGraphics();
	g.setXORMode(Color.red);
	g.drawRect(lastrect.x, lastrect.y, lastrect.width, lastrect.height);
	Applet.this.showStatus("");   // Clear the message.
	ImagemapRectangle r = findrect(e);
	if ((r != null) && (r == lastrect))  // If still in the same rectangle
	  Applet.this.getAppletContext().showDocument(r.url); // Go to the URL
	lastrect = null;
      }
    }

    /** Find the rectangle we're inside. */
    protected ImagemapRectangle findrect(MouseEvent e) {
      int i, x = e.getX(), y = e.getY();
      for(i = 0; i < rects.size(); i++)  {
	ImagemapRectangle r = (ImagemapRectangle) rects.elementAt(i);
	if (r.contains(x, y)) return r;
      }
      return null;
    }
  }

  /**
   * A helper class.  Just like java.awt.Rectangle, but with a URL field.
   * Note the use of a nested toplevel class for neatness.
   */
  static class ImagemapRectangle extends Rectangle {
    URL url;
    public ImagemapRectangle(int x, int y, int w, int h, URL url) {
      super(x, y, w, h);
      this.url = url;
    }
  }
}
