/************************************************}
{                                                }
{   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/4/04             }
{                                                }
{************************************************/

import java.awt.*;
import java.awt.image.*;
import java.applet.Applet;

/**
 * A <code>GrafPort</code> object implements QuickDraw-like drawing and 
 * event routines onto an object of class <code>DrawingApplet</code>, 
 * used for a drawing panel.
 * The <code>DrawingApplet</code> provides automatic repaint, using 
 * an off-screen image, and also listeners for keyboard and mouse events.
 * Usual <code>GrafPort</code> constructors initialize a 
 * <code>DrawingFrame</code>, which contains this <code>DrawingApplet</code>.
 *
 * @version  22 apr 2004
 * @author   Philippe Chassignet, Ecole Polytechnique
 * @see      DrawingFrame
 * @see      DrawingApplet
 * @see      MacLib
 */
public final class GrafPort {

  /**
   * Used to specify the normal painting mode.
   *
   * @see      #penMode(int)
   */
  public static final int PAT_COPY =  8;

  /**
   * Used to specify the normal painting mode.
   *
   * @see      #penMode(int)
   */
  public static final int PAT_XOR  = 10;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int BLACK_COLOR = 33;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int WHITE_COLOR = 30;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int RED_COLOR = 205;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int GREEN_COLOR = 341;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int BLUE_COLOR = 409;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int CYAN_COLOR = 273;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int MAGENTA_COLOR = 137;

  /**
   * Used to specify one of the standard painting color.
   *
   * @see      #foreColor(int)
   * @see      #backColor(int)
   */
  public static final int YELLOW_COLOR = 69;

  /**
   * Used to specify one of the standard text font.
   *
   * @see      #textFont(String)
   */
  public static final String DIALOG        = "Dialog";

  /**
   * Used to specify one of the standard text font.
   *
   * @see      #textFont(String)
   */
  public static final String DIALOG_INPUT  = "DialogInput";

  /**
   * Used to specify one of the standard text font.
   *
   * @see      #textFont(String)
   */
  public static final String SANS_SERIF    = "SansSerif";

  /**
   * Used to specify one of the standard text font.
   *
   * @see      #textFont(String)
   */
  public static final String SERIF         = "Serif";

  /**
   * Used to specify one of the standard text font.
   *
   * @see      #textFont(String)
   */
  public static final String MONOSPACED    = "Monospaced";

  /**
   * Used to specify a text style.
   *
   * @see      #textFace(int)
   */
  public static final int PLAIN	= Font.PLAIN;

  /**
   * Used to specify a text style.
   *
   * @see      #textFace(int)
   */
  public static final int BOLD = Font.BOLD;

  /**
   * Used to specify a text style.
   *
   * @see      #textFace(int)
   */
  public static final int ITALIC = Font.ITALIC;

  /**
   * Used to specify no modifier in the button state.
   *
   * @see      #buttonState()
   */
  public static final int NONE = DrawingStuff.NONE;

  /**
   * Used to encode the shift modifier in the button state.
   *
   * @see      #buttonState()
   */
  public static final int SHIFT = DrawingStuff.SHIFT;

  /**
   * Used to encode the control modifier in the button state.
   *
   * @see      #buttonState()
   */
  public static final int CONTROL = DrawingStuff.CONTROL;

  /**
   * Used to encode the meta modifier in the button state.
   *
   * @see      #buttonState()
   */
  public static final int META = DrawingStuff.META;

  /* working stuff */
  private int x_points[];
  private int y_points[];
  private Rect ovalRect;

  /* interface with the drawing stuff, the kind of frame depends on the 
     initialization. */
  private DrawingFrame frame;
  /* graphics and events are mainly accessed through an underlying 
     DrawingStuff. */
  private DrawingStuff stuff;
  private Graphics graphicsContext;

  /* local graphic context */
  private short current_x, current_y;
  private int paint_mode;
  private short pen_w, pen_h;
  private Color fore_color;
  private Color back_color;
  private String fontName;
  private short fontStyle;
  private short fontSize;
  private Font font;
  private FontMetrics metrics;

// Init

  // local stuff initialization
  private void init_graf() {
    x_points = new int[6];
    y_points = new int[6];
    ovalRect = new Rect();
    graphicsContext = stuff.getGraphics();
  }

  /**
   * Constructs and initializes a <code>GrafPort</code> and an attached 
   * <code>DrawingFrame</code> having :
   * <ul>
   * <li> the specified title,</li>
   * <li> the specified initial size for the frame,</li>
   * <li> the specified final size for the off-screen image</li>
   * </ul>
   *
   * @param       title  the name given to the created <code>DrawingFrame</code>
   * @param       width the initial width of the frame
   * @param       height the initial height of the frame
   * @param       fullWidth the final width of the off-screen image
   * @param       fullHeight the final height of the off-screen image
   */
  public GrafPort(String title, int width, int height, int fullWidth, int fullHeight) {
    frame = new DrawingFrame(this, title, width, height, fullWidth, fullHeight);
    stuff = frame.getStuff();
    init_graf();
    reset();
  }

  /**
   * Constructs and initializes a <code>GrafPort</code> and an attached 
   * <code>DrawingFrame</code> having :
   * <ul>
   * <li> the specified title,</li>
   * <li> the specified initial size for the frame,</li>
   * <li> the default final size for the off-screen image</li>
   * </ul>
   *
   * @param       title  the name given to the created <code>DrawingFrame</code>
   * @param       width the initial width of the frame
   * @param       height the initial height of the frame
   * @see         DrawingStuff#DEFAULT_SCREEN_WIDTH
   * @see         DrawingStuff#DEFAULT_SCREEN_HEIGHT
   */
  public GrafPort(String title, int width, int height) {
    this(title, width, height, DrawingStuff.DEFAULT_SCREEN_WIDTH, DrawingStuff.DEFAULT_SCREEN_HEIGHT);
  }

  /**
   * Constructs and initializes a <code>GrafPort</code> and an attached 
   * <code>DrawingFrame</code> having :
   * <ul>
   * <li> the specified title,</li>
   * <li> the default initial size for the frame,</li>
   * <li> the default size for the off-screen image</li>
   * </ul>
   *
   * @param       title  the name given to the created <code>DrawingFrame</code>
   * @see         DrawingFrame#DEFAULT_FRAME_WIDTH
   * @see         DrawingFrame#DEFAULT_FRAME_HEIGHT
   * @see         DrawingStuff#DEFAULT_SCREEN_WIDTH
   * @see         DrawingStuff#DEFAULT_SCREEN_HEIGHT
   */
  public GrafPort(String title) {
    this(title, DrawingFrame.DEFAULT_FRAME_WIDTH, DrawingFrame.DEFAULT_FRAME_HEIGHT);
  }

  /**
   * Constructs and initializes a <code>GrafPort</code> and an attached 
   * <code>DrawingFrame</code> having :
   * <ul>
   * <li> the title <code>"Drawing"</code>,</li>
   * <li> the default initial size for the frame,</li>
   * <li> the default size for the off-screen image</li>
   * </ul>
   *
   * @see         DrawingFrame#DEFAULT_FRAME_WIDTH
   * @see         DrawingFrame#DEFAULT_FRAME_HEIGHT
   * @see         DrawingStuff#DEFAULT_SCREEN_WIDTH
   * @see         DrawingStuff#DEFAULT_SCREEN_HEIGHT
   */
  public GrafPort() {
    this("Drawing");
  }

  /**
   * Constructs and initializes a <code>GrafPort</code> attached to the 
   * specified <code>DrawingApplet</code>.
   * This constructor is called during the initialization of the 
   * <code>DrawingApplet</code> if no <code>GrafPort</code> was already 
   * build. 
   * It is the case when the <code>DrawingApplet</code> is used as an applet.
   *
   * @param       drawingApplet  specifies an already existing 
   *                             <code>DrawingApplet</code> 
   *                             as the drawing panel
   */
  GrafPort(DrawingApplet drawingApplet) {
    frame = drawingApplet.getFrame();
    stuff = drawingApplet.getStuff();
    init_graf();
    reset();
  }

  /**
   * Resets the drawing context attached to this <code>GrafPort</code>.
   * Also, it clears the content of the drawing panel.
   */
  public void reset() {
    stuff.reset();
    current_x = 0; current_y = 0;
    pen_w = 1; pen_h = 1;
    paint_mode = PAT_COPY;
//    penPat(black);
    fore_color = Color.black;
    back_color = Color.white;
    setPaintMode(PAT_COPY, fore_color);
    fontName = SERIF;
    fontStyle = (short)PLAIN;
    fontSize = 12;
    setFont(fontName, fontStyle, fontSize);
  }

  /**
   * Disposes of this <code>GrafPort</code> and attached objects.
   */
  public synchronized void dispose() {
    if ( stuff != null ) {
      stuff.trace_if_verbose("disposing GrafPort") ;
      stuff = null;
    }
    metrics = null;
    if ( frame != null ) {
      frame.dispose(); //  enforces window closing
      frame = null;
    }
    if ( graphicsContext != null ) {
      graphicsContext.dispose();
      graphicsContext = null;
    }
  }

  /**
   * Called by the garbage collector when there are no more reference to 
   * this <code>GrafPort</code>.
   */
  public synchronized void finalize() {
    stuff.trace_if_verbose("finalizing GrafPort") ;
    if ( graphicsContext != null )
      dispose();
    // else dispose() is assumed to have been already explicitely called 
  }

// private expose

  private void exposeLine(int x1, int y1, int x2, int y2) {
    int min_x, max_x, min_y, max_y;

    if ( x1 < x2 )
      { min_x = x1; max_x = x2; }
    else
      { min_x = x2; max_x = x1; }
    if ( y1 < y2 )
      { min_y = y1; max_y = y2; }
    else
      { min_y = y2; max_y = y1; }
    stuff.doExpose(min_x, min_y, max_x+1, max_y+1);
  }

  private void exposePoints(int x[], int y[], int count) {
    int min_x, max_x, min_y, max_y;
    int i;

    min_x = min_y =  32767;
    max_x = max_y = -32768;
    for( i = 0; i < count; i++ ) {
      if ( x[i] < min_x )
        min_x = x[i];
      if ( x[i] > max_x )
        max_x = x[i];
      if ( y[i] < min_y )
        min_y = y[i];
      if ( y[i] > max_y )
        max_y = y[i];
    }
    stuff.doExpose(min_x, min_y, max_x, max_y);
  }

  private void exposeRect(Rect r) {
    stuff.doExpose(r.left, r.top, r.right, r.bottom);
  }

// private drawing context

  private void setPaintMode(int mode, Color color) {
    if ( mode == PAT_XOR ) {
      graphicsContext.setColor(Color.black);
      graphicsContext.setXORMode(Color.white);
    } else {
      graphicsContext.setPaintMode();
      graphicsContext.setColor(color);
    }
  }

  private static Color switchColor(int color) {
    switch(color) {
      case BLACK_COLOR:
        return Color.black;
      case WHITE_COLOR:
        return Color.white;
      case RED_COLOR:
        return Color.red;
      case GREEN_COLOR:
        return Color.green;
      case BLUE_COLOR:
        return Color.blue;
      case CYAN_COLOR:
        return Color.cyan;
      case MAGENTA_COLOR:
        return Color.magenta;
      case YELLOW_COLOR:
        return Color.yellow;
      default:
        return null;
    }
  }

  private void setFont(String name, short style, short size) {
    font = new Font(name, style, size);
    stuff.trace_if_verbose("setFont : "+font+"  ");//+font.getFontName());
    graphicsContext.setFont(font);
    metrics = graphicsContext.getFontMetrics();
  }

// private drawing lines

  private void thickLine(int x, int y) {
    int xD, yD;

    xD = current_x;
    yD = current_y;
    x = (short)(xD+x) - xD;
    y = (short)(yD+y) - yD;
    if ( ((x >= 0) && (y >= 0)) || ((x < 0) && (y < 0)) )
      xD += pen_w;
    x_points[0] = xD;
    y_points[0] = yD;
    xD += x;
    yD += y;
    x_points[1] = xD;
    y_points[1] = yD;
    if ( y >= 0 )
      yD += pen_h;
    else if ( x >= 0 )
      xD += pen_w;
    else
      xD -= pen_w;
    x_points[2] = xD;
    y_points[2] = yD;
    if ( y < 0 )
      yD += pen_h;
    else if ( x >= 0 )
      xD -= pen_w;
    else
      xD += pen_w;
    x_points[3] = xD;
    y_points[3] = yD;
    xD -= x;
    yD -= y;
    x_points[4] = xD;
    y_points[4] = yD;
    if ( y >= 0 )
      yD -= pen_h;
    else if ( x >= 0 )
      xD -= pen_w;
    else
      xD += pen_w;
    x_points[5] = xD;
    y_points[5] = yD;
    graphicsContext.fillPolygon(x_points, y_points, 6);
    exposePoints(x_points, y_points, 6);
  }

  private void thinLine(int x, int y) {
    short dest_x = (short)(current_x+x);
    short dest_y = (short)(current_y+y);
    graphicsContext.drawLine(current_x, current_y, dest_x, dest_y);
    exposeLine(current_x, current_y, dest_x, dest_y);
  }

// private flood shapes

  private void floodRect(Rect r) {
    graphicsContext.fillRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
    exposeRect(r);
  }

  private void floodOval(Rect r) {
    graphicsContext.fillArc(r.left, r.top, 
                     r.right - r.left - 1, r.bottom - r.top - 1,
                     0, 360);
    exposeRect(r);
  }

  private void floodRoundRect(Rect r, int arcWidth, int arcHeight) {
    graphicsContext.fillRoundRect(r.left, r.top, r.right - r.left, r.bottom - r.top, 
                           arcWidth, arcHeight);
    exposeRect(r);
  }

  private void floodArc(Rect r, int startAngle, int arcAngle) {
    graphicsContext.fillArc(r.left, r.top, 
                     r.right - r.left - 1, r.bottom - r.top - 1, 
                     90-startAngle, -arcAngle);
    exposeRect(r);
  }

  private void floodPoly(Point[] points, int pCount) {
    int xPoints[] = new int[pCount];
    int yPoints[] = new int[pCount];

    for (int i = 0; i < pCount; i++ ) {
      xPoints[i] = points[i].h;
      yPoints[i] = points[i].v;
    }
    graphicsContext.fillPolygon(xPoints, yPoints, pCount);
    exposePoints(xPoints, yPoints, pCount);
  }

// Windows

  /**
   * Sets the bounds of the attached drawing panel as specified by the 
   * given <code>Rect</code> object.
   *
   * @param       r  a <code>Rect</code> object that specifies the new 
   *                 screen coordinates
   * @see         #resize(int, int)
   * @see         #getDrawingRect()
   */
  public void setDrawingRect(Rect r) {
    frame.setPortBounds(r.getRectangle());
  }

  /**
   * Sets the bounds of the attached drawing panel as specified.
   *
   * @param       width   the width of the panel in pixels
   * @param       height  the height of the panel in pixels
   * @see         #setDrawingRect(Rect)
   */
  public void resize(int width, int height) {
    Rectangle r = frame.getPortBounds();
    frame.setPortBounds(new Rectangle(r.x, r.y, width, height));
  }

  /**
   * Gets the drawing bounds of the attached drawing panel.
   *
   * @return      a new <code>Rect</code> object filled with the screen 
   *                coordinates of the drawing area
   * @see         #setDrawingRect(Rect)
   */
  public Rect getDrawingRect() {
    return new Rect().setRect(frame.getPortBounds());
  }

  /**
   * Makes the attached drawing panel visible and brings it to the front.
   */
  public void showDrawing() {
    frame.setVisible(true);
  }

  /**
   * Hides the attached drawing panel.
   */
  public void hideDrawing() {
    frame.setVisible(false);
  }

// Mouse

  /**
   * Returns the state of the mouse button for the attached drawing panel.
   *
   * @return      <code>true</code> if the button is currently down after 
   *                being pressed in the drawing panel;
   *              <code>false</code> otherwise
   */
  public boolean button() {
    Thread.yield();
    return stuff.getButton();
  }

  /**
   * Waits until the mouse button is pressed in the attached drawing panel.
   */
  public void waitClickDown() {
    do { Thread.yield(); } while (!stuff.waitClick());
  }

  /**
   * Waits until the mouse button is released after being pressed in the 
   * attached drawing panel.
   */
  public void waitClickUp() {
    do { Thread.yield(); } while (stuff.waitClick());
  }

  /**
   * Returns the current mouse location (relative to the attached 
   * drawing panel) as a <code>Point</code>Point object.
   *
   * @return      the <code>Point</code>Point object giving the mouse location
   */
  public Point getMouse() {
    Thread.yield();
    return new Point().setPt(stuff.getMouse());
  }

  /**
   * Waits until the mouse button is pressed and then released in the 
   * attached drawing panel.
   * Returns the mouse location (relative to the drawing panel) at the 
   * release time as a Point object.
   *
   * @return      the <code>Point</code>Point object giving the mouse location
   */
  public Point getClick() {
    waitClickDown();
    waitClickUp();
    return new Point().setPt(stuff.getMouse());
  }

  /**
   * Waits until the mouse button is released, after being pressed, or until 
   * the mouse location is dragged.
   * Stores the issued mouse location (relative to the attached drawing panel) 
   * in the given point.
   *
   * @param       p  the <code>Point</code> object to be modified
   */
  public boolean trackMouse(Point p) {
    Thread.yield();
    p.setPt(stuff.waitMouse());
    return stuff.getButton();
  }

  /**
   * Returns the <code>int</code> value that describes the modifier keys state 
   * at the time of the last mouse pressed event.
   * The returned value is either <code>NONE</code>, or a combination of 
   * the <code>SHIFT</code>, <code>CONTROL</code> and <code>META</code> 
   * (for the <em>Alt</em> key) constant flags.
   *
   * @return      the modifier keys state as an <code>int</code> value
   * @see         #NONE
   * @see         #SHIFT
   * @see         #CONTROL
   * @see         #META
   */
  public int buttonState() {
    Thread.yield();
    return stuff.getButtonState();
  }

// KeyBoard

  /**
   * Checks whether the char, corresponding to the last keyboard event 
   * occured in the drawing panel, is still available.
   *
   * @return      <code>true</code> if any char available; 
   *              <code>false</code> otherwise
   */
  public boolean keyPressed() {
    Thread.yield();
    return stuff.peekKey() != 0;
  }

  /**
   * Returns the char corresponding to the last keyboard event in the 
   * drawing panel.
   * If no char is available, will block until a new keyboard event occurs.
   * The char is made unavailable for further calls.
   *
   * @return      the last key char
   */
  public char getKey() {
    Thread.yield();
    return stuff.getKey();
  }

// Drawing context

  /**
   * Sets the current transfer mode, used for drawing lines and 
   * painting shapes, to be one of the two constant flags 
   * <code>PAT_COPY</code> or <code>PAT_XOR</code>.
   * <p>
   * The initial pen mode is <code>PAT_COPY</code>.
   *
   * @param       mode  the specified painting mode
   * @see         #PAT_COPY
   * @see         #PAT_XOR
   */
  public void penMode(int mode) {
    paint_mode = mode;
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Sets the dimensions of the rectangular pen, used for drawing lines 
   * and framed shapes.
   * <p>
   * The initial pen size is <code>1x1</code>.
   *
   * @param       width    the new width for the pen
   * @param       height   the new height for the pen
   */
  public void penSize(int width, int height) {
    pen_w = (short)width;
    pen_h = (short)height;
  }

  /**
   * Resets the initial state, that is the mode and size, of the pen.
   * The pen location is not changed.
   *
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   */
  public void penNormal() {
    penMode(PAT_COPY);
//  penPat(black);
    penSize(1, 1);
  }

  /**
   * Sets the current pen color, used for drawing lines or text and painting 
   * shapes, as specified by the given <code>java.awt.Color</code> object.
   * <p>
   * The initial pen color is as specified by 
   * <code>java.awt.Color.black</code>.
   *
   * @param       color  the <code>Color</code> object
   */
  public void foreColor(Color color) {
    if (color == null )
      fore_color = Color.black;
    else
      fore_color = color;
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Sets the current pen color, used for drawing lines or text and painting 
   * shapes, as specified by one of the height constant flags :
   *    <code>BLACK_COLOR</code>,
   *    <code>WHITE_COLOR</code>,
   *    <code>RED_COLOR</code>,
   *    <code>GREEN_COLOR</code>,
   *    <code>BLUE_COLOR</code>,
   *    <code>CYAN_COLOR</code>,
   *    <code>MAGENTA_COLOR</code> or
   *    <code>YELLOW_COLOR</code>.
   * <p>
   * The initial pen color is as specified by <code>BLACK_COLOR</code>.
   *
   * @param       color  the specified color flag
   * @see         #BLACK_COLOR
   * @see         #WHITE_COLOR
   * @see         #RED_COLOR
   * @see         #GREEN_COLOR
   * @see         #BLUE_COLOR
   * @see         #CYAN_COLOR
   * @see         #MAGENTA_COLOR
   * @see         #YELLOW_COLOR
   */
  public void foreColor(int color) {
    foreColor(switchColor(color));
  }

  /**
   * Sets the current background color, used when erasing or inverting, 
   * as specified by the given <code>java.awt.Color</code> object.
   * <p>
   * The initial background color is as specified by 
   * <code>java.awt.Color.white</code>.
   *
   * @param       color  the <code>Color</code> object
   */
  public void backColor(Color color) {
    if (color == null )
      back_color = Color.white;
    else
      back_color = color;
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Sets the current background color, used when erasing or inverting, 
   * as specified by one of the height constant flags :
   *    <code>BLACK_COLOR</code>,
   *    <code>WHITE_COLOR</code>,
   *    <code>RED_COLOR</code>,
   *    <code>GREEN_COLOR</code>,
   *    <code>BLUE_COLOR</code>,
   *    <code>CYAN_COLOR</code>,
   *    <code>MAGENTA_COLOR</code> or
   *    <code>YELLOW_COLOR</code>.
   * <p>
   * The initial background color is as specified by 
   * <code>WHITE_COLOR</code>.
   *
   * @param       color  the specified color flag
   * @see         #BLACK_COLOR
   * @see         #WHITE_COLOR
   * @see         #RED_COLOR
   * @see         #GREEN_COLOR
   * @see         #BLUE_COLOR
   * @see         #CYAN_COLOR
   * @see         #MAGENTA_COLOR
   * @see         #YELLOW_COLOR
   */
  public void backColor(int color) {
    backColor(switchColor(color));
  }

  /**
   * Returns the current pen location (relative to the attached drawing panel) 
   * as a Point object.
   *
   * @return      the <code>Point</code> object giving the pen location
   */
  public Point getPen() {
    return new Point(current_x, current_y);
  }

  /**
   * Sets the coordinates of the pen (relative to the drawing panel) to the 
   * specified location.
   * <p>
   * No drawing is performed.
   *
   * @param       h  the horizontal coordinate of the new location
   * @param       v  the vertical coordinate of the new location
   */
  public void moveTo(int h, int v) {
    current_x = (short)h;
    current_y = (short)v;
  }

  /**
   * Translates the coordinates of the pen by the specified displacement 
   * vector.
   * <p>
   * No drawing is performed.
   *
   * @param       dh  the horizontal displacement
   * @param       dv  the vertical displacement
   */
  public void move(int dh, int dv) {
    current_x += dh;
    current_y += dv;
  }

  /**
   * Sets the name of the font used for drawing text. 
   * Some supported fonts are given by the followings constants :
   *    <code>DIALOG</code>,
   *    <code>DIALOG_INPUT</code>,
   *    <code>SANS_SERIF</code>,
   *    <code>SERIF</code> or
   *    <code>MONOSPACED</code>.
   * <p>
   * The initial text font is as specified by <code>SERIF</code>.
   *
   * @param       the name of the new font used for drawing text
   * @see         #DIALOG
   * @see         #DIALOG_INPUT
   * @see         #SANS_SERIF
   * @see         #SERIF
   * @see         #MONOSPACED
   */
  public void textFont(String name) {
    if ( !name.equals(fontName) ) {
      fontName = name;
      setFont(fontName, fontStyle, fontSize);
    }
  }

  /**
   * Sets the size of the font used for drawing text. 
   * <p>
   * The initial text size is <code>12</code>.
   *
   * @param       the new size for drawing text
   */
  public void textSize(int size) {
    if ( size != fontSize ) {
      fontSize = (short)size;
      setFont(fontName, fontStyle, fontSize);
    }
  }

  /**
   * Sets the style of the font used for drawing text, 
   * it should be one of the followings constants :
   *    <code>PLAIN</code>,
   *    <code>BOLD</code>,
   *    <code>ITALIC</code> or
   *    <code>BOLD+ITALIC</code>.
   * <p>
   * The initial text style is as specified by <code>PLAIN</code>.
   *
   * @param       the code of the new style for drawing text
   * @see         #PLAIN
   * @see         #BOLD
   * @see         #ITALIC
   */
  public void textFace(int face) {
    if ( face != fontStyle ) {
      fontStyle = (short)face;
      setFont(fontName, fontStyle, fontSize);
    }
  }

  /**
   * Returns the metric informations about the current font as a 
   * <code>FontInfo</code> object.
   *
   * @return      the <code>FontInfo</code> object
   */
  public FontInfo getFontInfo() {
    return new FontInfo((short)metrics.getAscent(), (short)metrics.getDescent(), 
                        (short)metrics.getMaxAdvance(), (short)metrics.getLeading());
  }

// Drawing lines

  /**
   * Draws a line from the current pen location to the location that is 
   * at distance of dh horizontally and dv vertically.
   * The pen location becomes this new location.
   *
   * @param       dh  the horizontal displacement
   * @param       dv  the vertical displacement
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void line(int dh, int dv) {
    if ( (pen_w == 1) && (pen_h == 1) )
      thinLine(dh, dv);
    else
      thickLine(dh, dv);
    move(dh, dv);
  }

  /**
   * Draws a line from the current pen location to the location that is 
   * specified by h and v (in coordinates relative to the drawing panel).
   * The pen location becomes this new location.
   *
   * @param       h  the horizontal coordinate of the new location
   * @param       v  the vertical coordinate of the new location
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void lineTo(int h, int v) {
    line((short)h-current_x, (short)v-current_y);
  }

  /**
   * Draws a line from the location that is specified by x1 and y1 to 
   * the location that is specified by x2 and y2 (in coordinates relative 
   * to the drawing panel).
   * The pen location becomes the second location.
   *
   * @param       x1  the horizontal coordinate of the first location
   * @param       y1  the vertical coordinate of the first location
   * @param       x2  the horizontal coordinate of the second location
   * @param       y2  the vertical coordinate of the second location
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void drawLine(int x1, int y1, int x2, int y2) {
    moveTo(x1, y1);
    lineTo(x2, y2);
  }

// Shapes

  /**
   * Draws an outline just inside the specified rectangle.
   * The pen location is not changed.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the rectangle to be framed
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void frameRect(Rect r) {
    if ( r.emptyRect() )
      return;
    if ( (r.right - r.left <= 2*pen_w) || (r.bottom - r.top <= 2*pen_h) )
      paintRect(r);
    else {
      short save_x = current_x;
      short save_y = current_y;
      drawLine(r.left, r.top, r.right - 2*pen_w, r.top);
      drawLine(r.right - pen_w, r.top, r.right - pen_w, r.bottom - 2*pen_h);
      drawLine(r.right - pen_w, r.bottom - pen_h, 
               r.left + pen_w, r.bottom - pen_h);
      drawLine(r.left, r.bottom - pen_h, r.left, r.top + pen_h);
      moveTo(save_x, save_y);
    }
  }

  /**
   * Paints inside the specified rectangle.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the rectangle to be painted
   * @see         #penMode(int)
   * @see         #foreColor(java.awt.Color)
   */
  public void paintRect(Rect r) {
    floodRect(r);
  }

  /**
   * Paints inside the specified rectangle, using the background color.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the rectangle to be erased
   * @see         #backColor(java.awt.Color)
   */
  public void eraseRect(Rect r) {
    setPaintMode(PAT_COPY, back_color);
    floodRect(r); 
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Inverts pixels inside the specified rectangle.
   * Every white pixel becomes black and reciprocally.
   * Pixels of other colors are changed in an undefined but reversible 
   * manner.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the rectangle to be inverted
   */
  public void invertRect(Rect r) {
    setPaintMode(PAT_XOR, fore_color);
    floodRect(r); 
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Draws an outline just inside the oval that fits inside the 
   * specified rectangle.
   * The pen location is not changed.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the oval to be framed
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void frameOval(Rect r) {
    if ( r.emptyRect() )
      return;
    if ( (r.right - r.left <= 2*pen_w) || (r.bottom - r.top <= 2*pen_h) )
      paintOval(r);
    else {
      graphicsContext.drawArc(r.left, r.top,
                       r.right - r.left - 1, r.bottom - r.top - 1,
                       0, 360);
      exposeRect(r);
    }
  }

  /**
   * Paints inside the oval that fits inside the specified rectangle.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the oval to be painted
   * @see         #penMode(int)
   * @see         #foreColor(java.awt.Color)
   */
  public void paintOval(Rect r) {
    floodOval(r);
  }

  /**
   * Paints inside the oval that fits inside the specified rectangle, 
   * using the background color.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the oval to be erased
   * @see         #backColor(java.awt.Color)
   */
  public void eraseOval(Rect r) {
    setPaintMode(PAT_COPY, back_color);
    floodOval(r); 
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Inverts pixels inside the oval that fits inside the specified rectangle.
   * Every white pixel becomes black and reciprocally.
   * Pixels of other colors are changed in an undefined but reversible 
   * manner.
   *
   * @param       r  a <code>Rect</code> object that specifies 
   *                 the oval to be inverted
   */
  public void invertOval(Rect r) {
    setPaintMode(PAT_XOR, fore_color);
    floodOval(r); 
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Paints inside the specified circle.
   *
   * @param       x  the <em>x</em> coordinate of the center of the circle
   * @param       y  the <em>y</em> coordinate of the center of the circle
   * @param       r  the radius of the circle
   * @see         #penMode(int)
   * @see         #foreColor(java.awt.Color)
   */
  public void paintCircle(int x, int y, int r) {
    ovalRect.setRect(x-r, y-r, x+r, y+r);
    floodOval(ovalRect);
  }

  /**
   * Inverts pixels inside the specified circle.
   * Every white pixel becomes black and reciprocally.
   * Pixels of other colors are changed in an undefined but reversible 
   * manner.
   *
   * @param       x  the <em>x</em> coordinate of the center of the circle
   * @param       y  the <em>y</em> coordinate of the center of the circle
   * @param       r  the radius of the circle
   */
  public void invertCircle(int x, int y, int r) {
    ovalRect.setRect(x-r, y-r, x+r, y+r);
    setPaintMode(PAT_XOR, fore_color);
    floodOval(ovalRect); 
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Draws an outline just inside the specified rounded-corner rectangle.
   * The pen location is not changed.
   *
   * @param       r          a <code>Rect</code> object that specifies 
   *                         the shape to be framed
   * @param       arcWidth   the horizontal diameter of curvature for the 
                             corners arcs
   * @param       arcHeight  the vertical diameter of curvature for the 
                             corners arcs
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void frameRoundRect(Rect r, int arcWidth, int arcHeight) {
    if ( r.emptyRect() )
      return;
    if ( (r.right - r.left <= 2*pen_w) || (r.bottom - r.top <= 2*pen_h) )
      paintRoundRect(r, arcWidth, arcHeight);
    else {
      graphicsContext.drawRoundRect(r.left, r.top, 
                             r.right - r.left, r.bottom - r.top, 
                             arcWidth, arcHeight);
      exposeRect(r);
    }
  }

  /**
   * Paints inside the specified rounded-corner rectangle.
   *
   * @param       r          a <code>Rect</code> object that specifies 
   *                         the shape to be painted
   * @param       arcWidth   the horizontal diameter of curvature for the 
                             corners arcs
   * @param       arcHeight  the vertical diameter of curvature for the 
                             corners arcs
   * @see         #penMode(int)
   * @see         #foreColor(java.awt.Color)
   */
  public void paintRoundRect(Rect r, int arcWidth, int arcHeight) {
    floodRoundRect(r, arcWidth, arcHeight);
  }

  /**
   * Paints inside the specified rounded-corner rectangle, 
   * using the background color.
   *
   * @param       r          a <code>Rect</code> object that specifies 
   *                         the shape to be erased
   * @param       arcWidth   the horizontal diameter of curvature for the 
                             corners arcs
   * @param       arcHeight  the vertical diameter of curvature for the 
                             corners arcs
   * @see         #backColor(java.awt.Color)
   */
  public void eraseRoundRect(Rect r, int arcWidth, int arcHeight) {
    setPaintMode(PAT_COPY, back_color);
    floodRoundRect(r, arcWidth, arcHeight);
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Inverts pixels inside the specified rounded-corner rectangle.
   * Every white pixel becomes black and reciprocally.
   * Pixels of other colors are changed in an undefined but reversible 
   * manner.
   *
   * @param       r          a <code>Rect</code> object that specifies 
   *                         the shape to be inverted
   * @param       arcWidth   the horizontal diameter of curvature for the 
                             corners arcs
   * @param       arcHeight  the vertical diameter of curvature for the 
                             corners arcs
   */
  public void invertRoundRect(Rect r, int arcWidth, int arcHeight) {
    setPaintMode(PAT_XOR, fore_color);
    floodRoundRect(r, arcWidth, arcHeight);
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Draws an arc of outline just inside the oval that fits inside the 
   * specified rectangle.
   * The startAngle parameter indicates where the arc begins and is 
   * treated mod 360.
   * The arcAngle parameters defines the extent of the arc.
   * The angles are given in degrees. A positive angle goes clockwise.
   * Zero degree is at l2 o'clock.
   * The pen location is not changed.
   *
   * @param       r           a <code>Rect</code> object that specifies 
   *                          the shape to be framed
   * @param       startAngle  indicates where the arc begins
   * @param       arcAngle    defines the extent of the arc
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void frameArc(Rect r, int startAngle, int arcAngle) {
    if ( r.emptyRect() )
      return;
    if ( (r.right - r.left <= 2*pen_w) || (r.bottom - r.top <= 2*pen_h) )
      paintArc(r, startAngle, arcAngle);
    else {
      graphicsContext.drawArc(r.left, r.top, 
                       r.right - r.left - 1, r.bottom - r.top - 1,
                       90-startAngle, -arcAngle);
      exposeRect(r);
    }
  }

  /**
   * Paints inside a wedge of the oval that fits inside the specified 
   * rectangle.
   * The startAngle parameter indicates where the arc begins and is 
   * treated mod 360.
   * The arcAngle parameters defines the extent of the arc.
   * The angles are given in degrees. A positive angle goes clockwise.
   * Zero degree is at l2 o'clock.
   *
   * @param       r           a <code>Rect</code> object that specifies 
   *                          the shape to be painted
   * @param       startAngle  indicates where the arc begins
   * @param       arcAngle    defines the extent of the arc
   * @see         #penMode(int)
   * @see         #foreColor(java.awt.Color)
   */
  public void paintArc(Rect r, int startAngle, int arcAngle) {
    floodArc(r, startAngle, arcAngle);
  }

  /**
   * Paints inside a wedge of the oval that fits inside the specified 
   * rectangle, using the background color.
   * The startAngle parameter indicates where the arc begins and is 
   * treated mod 360.
   * The arcAngle parameters defines the extent of the arc.
   * The angles are given in degrees. A positive angle goes clockwise.
   * Zero degree is at l2 o'clock.
   *
   * @param       r           a <code>Rect</code> object that specifies 
   *                          the shape to be erased
   * @param       startAngle  indicates where the arc begins
   * @param       arcAngle    defines the extent of the arc
   * @see         #backColor(java.awt.Color)
   */
  public void eraseArc(Rect r, int startAngle, int arcAngle) {
    setPaintMode(PAT_COPY, back_color);
    floodArc(r, startAngle, arcAngle);
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Inverts pixels inside a wedge of the oval that fits inside the specified 
   * rectangle.
   * Every white pixel becomes black and reciprocally.
   * Pixels of other colors are changed in an undefined but reversible 
   * manner.
   * The startAngle parameter indicates where the arc begins and is 
   * treated mod 360.
   * The arcAngle parameters defines the extent of the arc.
   * The angles are given in degrees. A positive angle goes clockwise.
   * Zero degree is at l2 o'clock.
   *
   * @param       r           a <code>Rect</code> object that specifies 
   *                          the shape to be inverted
   * @param       startAngle  indicates where the arc begins
   * @param       arcAngle    defines the extent of the arc
   */
  public void invertArc(Rect r, int startAngle, int arcAngle) {
    setPaintMode(PAT_XOR, fore_color);
    floodArc(r, startAngle, arcAngle);
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Draws an outline of the specified polygon.
   *
   * @param       points  an array of the <code>Point</code> objects that 
                          specify the polygon to be framed
   * @param       pCount  the number of points
   * @see         MacLib#newPointArray(int)
   * @see         #penMode(int)
   * @see         #penSize(int, int)
   * @see         #foreColor(java.awt.Color)
   */
  public void framePolygon(Point[] points, int pCount) {
    moveTo(points[0].h, points[0].v);
    for( int i=1; i<pCount; i++ )
      lineTo(points[i].h, points[i].v);
    lineTo(points[0].h, points[0].v);
  }

  /**
   * Paints inside the specified polygon.
   * The area inside the polygon is defined using the even-odd fill rule.
   *
   * @param       points  an array of the <code>Point</code> objects that 
                          specify the polygon to be painted
   * @param       pCount  the number of points
   * @see         MacLib#newPointArray(int)
   * @see         #penMode(int)
   * @see         #foreColor(java.awt.Color)
   */
  public void paintPolygon(Point[] points, int pCount) {
    floodPoly(points, pCount);
  }

  /**
   * Paints inside the specified polygon, using the background color.
   * The area inside the polygon is defined using the even-odd fill rule.
   *
   * @param       points  an array of the <code>Point</code> objects that 
                          specify the polygon to be erased
   * @param       pCount  the number of points
   * @see         MacLib#newPointArray(int)
   * @see         #backColor(java.awt.Color)
   */
  public void erasePolygon(Point[] points, int pCount) {
    setPaintMode(PAT_COPY, back_color);
    floodPoly(points, pCount);
    setPaintMode(paint_mode, fore_color);
  }

  /**
   * Inverts pixels inside the specified polygon.
   * The area inside the polygon is defined using the even-odd fill rule.
   * Every white pixel becomes black and reciprocally.
   * Pixels of other colors are changed in an undefined but reversible 
   * manner.
   *
   * @param       points  an array of the <code>Point</code> objects that 
                          specify the polygon to be inverted
   * @param       pCount  the number of points
   * @see         MacLib#newPointArray(int)
   */
  public void invertPolygon(Point[] points, int pCount) {
    setPaintMode(PAT_XOR, fore_color);
    floodPoly(points, pCount);
    setPaintMode(paint_mode, fore_color);
  }

// Text

  /**
   * Returns the width in pixels of the given text string.
   * This value is added to the pen horizontal coordinate when the 
   * string is drawn.
   *
   * @param       s  the string to be measured 
   * @return      the width of the given string 
   * @see         #drawString(java.lang.String)
   */
  public int stringWidth(String s) {
    if ( s != null )
      return metrics.stringWidth(s);
    else
      return 0;
  }

  /**
   * Returns the width in pixels of the specified text.
   * This value is added to the pen horizontal coordinate when the 
   * specified text is drawn.
   *
   * @param       textBuf    the array of characters that stores the text
   * @param       firstByte  indicates where the measured text begins
   * @param       byteCount  indicates the number of characters to be measured
   * @return      the width of the specified text
   * @see         #drawText(char[], int, int)
   */
  public int textWidth(char textBuf[], int firstByte, int byteCount) {
	return metrics.charsWidth(textBuf, firstByte, byteCount);
  }

  /**
   * Returns the width in pixels of the given character.
   * This value is added to the pen horizontal coordinate when the 
   * char is drawn.
   *
   * @param       c  the character to be measured
   * @return      the width of the given character
   * @see         #drawChar(char)
   */
  public int charWidth(char c) {
    return metrics.charWidth(c);
  }

  /**
   * Draws the given text string.
   * The string is placed beginning at the current pen location and 
   * extending to the right.
   * The pen location ends up to the right of the last character in the string.
   *
   * @param       s  the string to be drawn
   * @see         #moveTo(int, int)
   * @see         #foreColor(java.awt.Color)
   * @see         #textFont(java.lang.String)
   * @see         #textSize(int)
   * @see         #textFace(int)
   */
  public void drawString(String s) {
    if ( s != null ) {
      short start_x = current_x;
      setPaintMode(PAT_COPY, fore_color);
      graphicsContext.drawString(s, current_x, current_y);
      setPaintMode(paint_mode, fore_color);
      move(metrics.stringWidth(s), 0);
      stuff.doExpose(start_x, current_y-metrics.getMaxAscent(), 
                     current_x, current_y+metrics.getMaxDescent());
    }
  }

  /**
   * Draws the specified text.
   * The text is placed beginning at the current pen location and 
   * extending to the right.
   * The pen location ends up to the right of the last character in the string.
   *
   * @param       textBuf    the array of characters that stores the text
   * @param       firstByte  indicates where the drawn text begins
   * @param       byteCount  indicates the number of characters to be drawn
   * @see         #moveTo(int, int)
   * @see         #foreColor(java.awt.Color)
   * @see         #textFont(java.lang.String)
   * @see         #textSize(int)
   * @see         #textFace(int)
   */
  public void drawText(char textBuf[], int firstByte, int byteCount) {
    drawString(new String(textBuf, firstByte, byteCount));
  }

  /**
   * Draws the given character.
   * The char is placed beginning at the current pen location and 
   * extending to the right.
   * The pen location ends up to the right of the character.
   *
   * @param       c  the character to be drawn
   * @see         #moveTo(int, int)
   * @see         #foreColor(java.awt.Color)
   * @see         #textFont(java.lang.String)
   * @see         #textSize(int)
   * @see         #textFace(int)
   */
  public void drawChar(char c) {
    drawString(""+c);
  }

// Image

  /**
   * Creates an image from the specified image producer.
   * @param     producer  the image producer
   * @return    the image produced.    
   */
  public Image createImage(ImageProducer producer) {
    return frame.createImage(producer);
  }

  /**
   * Creates an off-screen drawable image 
   *     to be used for double buffering.
   * @param     width the specified width.
   * @param     height the specified height.
   * @return    an off-screen drawable image, 
   *            which can be used for double buffering.
  */
  public Image createImage(int width, int height) {
    return frame.createImage(width, height);
  }

  /**
   * Returns the drawing panel attached to this <code>GrafPort</code>.
   */
  public DrawingApplet getPanel() {
    return frame.getPanel();
  }

  /**
   * Draws the given image,
   * starting at location <code>(x,y)</code> and scaled to fit 
   * a <code>width x height</code> rectangle.
   *
   * @param       img     the <code>Image</code> object to be drawn
   * @param       x       the <em>x</em> coordinate of the top-left corner
   * @param       y       the <em>y</em> coordinate of the top-left corner
   * @param       width   the width of the drawn image
   * @param       height  the height of the drawn image
   * @return      <code>true</code> if the image has been completely drawn.
   * @see         java.awt.Graphics#drawImage(Image, int, int, int, int, java.awt.image.ImageObserver)
   */
  public boolean drawImage(Image img, int x, int y, int width, int height) {
    if ( width < 0 )
      width = img.getWidth(frame);
    if ( height < 0 )
      height = img.getHeight(frame);
    boolean done = graphicsContext.drawImage(img, x, y, width, height, frame);
    stuff.doExpose(x, y, x+width, y+height);
    return done;
  }

  /**
   * Draws the given image, starting at location <code>(x,y)</code>.
   *
   * @param       img     the <code>Image</code> object to be drawn
   * @param       x       the <em>x</em> coordinate of the top-left corner
   * @param       y       the <em>y</em> coordinate of the top-left corner
   * @return      <code>true</code> if the image has been completely drawn.
   * @see         java.awt.Graphics#drawImage(Image, int, int, java.awt.image.ImageObserver)
   */
  public boolean drawImage(Image img, int x, int y) {
    return drawImage(img, x, y, -1, -1);
  }

  /**
   * Prints a string of text (for user information).
   * In the case of a <code>GrafPort</code> explicitely constructed, 
   * the string is printed on <code>System.out</code>.
   * In the case of the <code>GrafPort</code> that is initialized by a  
   * <code>DrawingApplet</code>, the string is forwarded to the 
   * <code>showStatus</code> method of the applet.
   *
   * @param       s  the string to be printed
   * @see         java.applet.Applet#showStatus(java.lang.String)
   */
  public void showStatus(String s) {
    frame.showStatus(s);
  }

  /**
   * Returns a string representation of the state of this 
   * <code>GrafPort</code>.
   *
   * @return      the string representation of internal parameters
   */
  public String toString() {
    return getClass().getName() + "{frame=" + frame + "}";
  }

}  // end class GrafPort

