/************************************************}
{                                                }
{   JDK 1.1, 1.2, 1.3, 1.4                       }
{   Extensions graphiques compatibles QuickDraw  }
{   P.Chassignet, Ecole Polytechnique, 1998-2004 }
{                                                }
{   dernie`re mise a` jour:  22/3/04             }
{                                                }
{************************************************/

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

/**
 * A <code>DrawingApplet</code> is used for a drawing panel and it is associated 
 * with a <code>DrawingStuff</code> object that provides automatic repaint, using 
 * an off-screen image.
 * The <code>DrawingStuff</code> also implements listeners for handling keyboard 
 * and mouse events that concern the <code>DrawingApplet</code>.
 * <p>
 * A subclass of <code>DrawingApplet</code> may be instanciated directly as an 
 * applet.
 * In this case, a <code>GrafPort</code> object is also allocated and it provides an 
 * interface for QuickDraw-like drawing and event routines.
 * This <code>GrafPort</code> is available by a call to the <code>getPort()</code> 
 * method.
 * Any subclass of <code>DrawingApplet</code> that overrides the  
 * <code>init()</code>, <code>destroy()</code> or <code>paint(Graphics)</code> 
 * methods, should call these in turn to provides suitable working.
 * The <code>start()</code> method does nothing and any subclass of 
 * <code>DrawingApplet</code> should override it.
 * <p>
 * A <code>DrawingApplet</code> object is also instanciated during the explicit 
 * initialization of a <code>GrafPort</code>.
 * In that case the <code>DrawingApplet</code> is set as the component of a 
 * <code>DrawingFrame</code> and it doesn't need direct control.
 *
 * @version  22 mar 2004
 * @author   Philippe Chassignet, Ecole Polytechnique
 * @see      DrawingStuff
 * @see      GrafPort
 * @see      DrawingFrame
 */

public class DrawingApplet extends Applet
  implements ComponentListener {

  // the GrafPort object is available through a call to getPort.
  private GrafPort port;
  // the DrawingFrame object is available through a call to getFrame.
  private DummyFrame frame;
  // the DrawingStuff object is available through a call to getStuff.
  private DrawingStuff stuff;
  private Dimension size;
  private boolean isApplet;

  /**
   * The default constructor for a <code>DrawingApplet</code> used as an applet.
   * A <code>GrafPort</code> will be later allocated during the call to the 
   * <code>init()</code> method.
   *
   * @see      #init()
   */
  public DrawingApplet() {
    port = null;
    frame = new DummyFrame(this); // A DummyFrame is allocated in place of a real DrawingFrame.
    isApplet = true;              // An AppletStub is already allocated.
  }

  /**
   * The constructor for a <code>DrawingApplet</code> when put inside a 
   * <code>DrawingFrame</code>.
   * A <code>GrafPort</code> and the parent <code>DrawingFrame</code> are already 
   * allocated, the <code>init(int, int)</code> method will be later explicitely 
   * called by the <code>DrawingFrame</code> constructor.
   *
   * @param       the already allocated <code>GrafPort</code>.
   * @param       width      the initial width of the panel
   * @param       height     the initial height of the panel
   */
  DrawingApplet(GrafPort g, int width, int height) {
    port = g;
    frame = null;
    isApplet = false;
    setStub(new DrawingStub(this));
    prefSize(new Dimension(width, height));
  }

  /**
   * Called by <code>init()</code> or explicitely called from a 
   * <code>DrawingFrame</code> constructor.
   * The given parameters are the final dimensions for the off-screen image.
   * It returns the <code>DrawingStuff</code> object.
   *
   * @param       width  the final width of the off-screen image
   * @param       height the final height of the off-screen image
   */
  DrawingStuff init(int width, int height) {
    if ( getParent() != null )
      setName(getParent().getName()+"."+getName());
    setBackground(Color.white);
    setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
    stuff = new DrawingStuff(this, width, height, getParameter("MACLIB_DEBUG"));
    addMouseListener(stuff);
    addMouseMotionListener(stuff);
    addKeyListener(stuff);
    if ( port == null )
      port = new GrafPort(this);
    addComponentListener(this);
    return stuff;
  }

  /**
   * The init method as for any applet.
   * It is called by the browser or applet viewer to inform this applet 
   * that it has been loaded into the system. It is always called before 
   * the first time that the <code>start()</code> method is called.
   * <p>
   * Any subclass of <code>DrawingApplet</code> that overrides this method, 
   * should call this in turn to provides suitable initialization.
   *
   * @see     java.applet.Applet#init()
   */
  public void init() {
    init(DrawingStuff.DEFAULT_SCREEN_WIDTH, DrawingStuff.DEFAULT_SCREEN_HEIGHT);
    ((DummyFrame)frame).setStuff(stuff);
  }

  /**
   * The start method as for any applet.
   * Called by the browser or applet viewer to inform this applet 
   * that it should start its execution. It is called after 
   * the <code>init()</code> method and each time the applet is revisited 
   * in a Web page.
   * <p>
   * This method does nothing and any subclass of <code>DrawingApplet</code> should 
   * override it, to perform any drawings each time the Web page containing it is 
   * visited.
   *
   * @see     java.applet.Applet#start()
   */
  public void start() {
    trace_if_verbose("starting DrawingApplet") ;
  }

  /**
   * The stop method as for any applet.
   * Called by the browser or applet viewer to inform this applet 
   * that it should stop its execution. It is called when 
   * the Web page that contains this applet has been replaced by 
   * another page, and also just before the applet is to be destroyed.
   * <p>
   * This method does nothing and any subclass of <code>DrawingApplet</code> has 
   * to override it, when stop processing is needed.
   *
   * @see     java.applet.Applet#stop()
   */
  public void stop() {
    trace_if_verbose("stopping DrawingApplet") ;
  }

  /**
   * The destroy method as for any applet. 
   * Called by the browser or applet viewer to inform this applet 
   * that it is being reclaimed and that it should destroy any resources 
   * that it has allocated. The <code>stop()</code> method will always 
   * be called before <code>destroy()</code>.
   * <p>
   * Any subclass of <code>DrawingApplet</code> that overrides this method, 
   * should call this in turn to provides suitable memory cleaning.
   *
   * @see     java.applet.Applet#destroy()
   */
  public void destroy() {
    if ( stuff != null ) {
      trace_if_verbose("destroying DrawingApplet") ;
      dispose();
    }
  }

  /**
   * Returns a string giving informations about this DrawingApplet.
   * <p>
   * Any subclass of <code>DrawingApplet</code> that overrides this method, 
   * should include this one. 
   *
   * @return      the string of informations
   */
  public String getAppletInfo() {
	return "DrawingApplet implements QuickDraw-like drawing routines\n"
         + "P.Chassignet, Ecole Polytechnique, 2001-2003";
  }

  /**
   * Returns the DrawingFrame attached to this DrawingApplet.
   * for true applet
   */
  DummyFrame getFrame() {
    return frame;
  }

  /**
   * Returns the attached <code>DrawingStuff</code> object that provides 
   * drawings with automatic repaint and interfaces for keyboard and mouse 
   * events.
   *
   * @return      the <code>DrawingStuff</code> object
   */
  public DrawingStuff getStuff() {
    return stuff;
  }

  /**
   * Returns the <code>GrafPort</code> object that provides QuickDraw-like 
   * drawing and event routines onto this panel.
   *
   * @return      the <code>GrafPort</code> object
   */
  public GrafPort getPort() {
//    port.reset();
    return port;
  }

  /**
   * Disposes of this <code>DrawingApplet</code>.
   * This method must be called to release the resources.
   */
  public synchronized void dispose() {
    trace_if_verbose("disposing "+isApplet+" DrawingApplet");
    setVisible(false);
    if ( isApplet && port != null ) {
      port.dispose();
      port = null;
    }
    removeKeyListener(stuff);
    removeMouseMotionListener(stuff);
    removeMouseListener(stuff);
    if ( stuff != null ) {
      stuff.dispose();
      stuff = null;
    }
    trace_if_verbose("DrawingApplet disposed");
  }
 
  /**
   * Called by the garbage collector when there are no more reference to 
   * this <code>DrawingApplet</code>.
   */
  public synchronized void finalize() {
    if ( stuff != null )
      dispose();
    // else dispose() is assumed to have been already explicitely called 
  }

  public void prefSize(Dimension size) {
    trace_if_verbose("setPreferredSize -> "+size);
    this.size = size;
  }

  public Dimension getPreferredSize() {
    trace_if_verbose("getPreferredSize -> "+size);
//	return preferredSize();
	return size;
  }

  /**
   * Invoked by the event handler when this panel needs to be repaint.
   * It results in copying the content of the attached off-screen image. 
   * <p>
   * Any subclass of <code>DrawingApplet</code> that overrides this method, 
   * should call this in turn to provides suitable update.
   *
   * @param       g  the specified <code>Graphics</code> object for painting onto 
   *                   this panel
   */
  public void paint(Graphics g) {
    if ( stuff != null )
      stuff.paint(g);
  }

  public void update(Graphics g) {
    paint(g);
  }

// ComponentListener

  /**
   * Invoked when the panel has been resized.
   */
  public void componentResized(ComponentEvent e) {
    prefSize(getSize());
    trace_if_verbose("panel resized "+getBounds());
  }

  /**
   * Invoked when the panel has been moved.
   */    
  public void componentMoved(ComponentEvent e) {
    trace_if_verbose("panel moved "+getBounds());
  }

  /**
   * Invoked when the panel has been made visible.
   */
  public void componentShown(ComponentEvent e) {
    trace_if_verbose("panel shown "+getBounds());
  }

  /**
   * Invoked when the panel has been hidden.
   */
  public void componentHidden(ComponentEvent e) {
    trace_if_verbose("panel hidden "+getBounds());
  }

  /**
   * Returns a string representation of the state of this <code>DrawingApplet</code>.
   * Invoked by the inherited method <code>toString()</code>.
   *
   * @return      the string representation of internal parameters.
   */
  protected String paramString() {
	return super.paramString() + "," + stuff;
  }

  /**
   * Prints a string when verbose mode is enabled.
   * For internal debug purpose only.
   */
  void trace_if_verbose(String str) {
    if ( stuff != null )
      stuff.trace_if_verbose(str);
  }

}  // end class DrawingApplet


// A DrawingStub is used when the DrawingApplet is instanciated inside a DrawingFrame.
class DrawingStub implements AppletStub {

  private DrawingApplet panel;

  public DrawingStub(DrawingApplet panel) {
    this.panel = panel;
  }

  public AppletContext getAppletContext() {
    System.err.println("getAppletContext");
    return null;
  }

  public URL getDocumentBase() {
    System.err.println("getDocumentBase");
    return null;
  }

  public URL getCodeBase() {
    System.err.println("getCodeBase");
    return null;
  }

  public void appletResize(int width, int height) {
    panel.trace_if_verbose("appletResize "+width+" "+height);
    panel.prefSize(new Dimension(width, height));
  }

  public String getParameter(String name) {
    String value;
    try {
      value = System.getProperty(name);
    } catch (SecurityException e) { value = null; }
    if ( !name.equals("MACLIB_DEBUG") )
      System.err.println("getParameter "+name);
    return value;
  }

  public boolean isActive() {
    System.err.println("isActive");
    return false;
  }

}  // end class DrawingStub

// A DummyFrame is used when the DrawingApplet is instanciated as an applet.
class DummyFrame extends DrawingFrame {
  private DrawingApplet applet;
  private DrawingStuff stuff;

  DummyFrame(DrawingApplet applet) {
    super(false);
    this.applet = applet;
  }

  void setStuff(DrawingStuff stuff) {
    this.stuff = stuff;
  }

  /**
   * Prints a string in status line.
   *
   * @param       s  the string to be printed.
   */
  public void showStatus(String s) {
    applet.showStatus(s);
  }

  /**
   * Shows or hides the applet panel depending on the parameter.
   *
   * @param b  if true, shows, 
   *           if false, hides.
   */
  public void setVisible(boolean b) {
    	applet.setVisible(b);
  }

  /**
   * Returns a string representation of the state of this DummyFrame.
   *
   * @return      the string representation of internal parameters.
   */
  public String toString() {
    return "DummyFrame applet=" + applet + "}";
  }

}  // end class DummyFrame

