/** Extended from the DoodlePad example in Exploring Java,
 * by Patrick Niemeyer and Joshua Peck,
 * First edition Copyright 1996 O'Reilly Publishing.
 * <P>
 * Object-oriented shape drawing from OOPDraw2.java,
 * Written By: Sunit Katkar
 * E-Mail:sunitkatkar@hotmail.com
 * Home-Page : www.vidyut.com/sunit
 * Java Page : www.vidyut.com/sunit/JavaPage.html
 */

import java.awt.*;
import java.applet.*;

public class Doodle2 extends Applet {

  DrawingPad2 dPad;

  Button
         clearButton,
         biggerButton, smallerButton,
         undoButton;

  Checkbox
	 writeHButton, writeVButton,
         drawButton, lineButton, rectangleButton, ovalButton,
         redButton, yellowButton, greenButton, blueButton;

  CheckboxGroup
	 writeHVGroup = new CheckboxGroup (),
         colorsGroup = new CheckboxGroup (),
         modesGroup = new CheckboxGroup ();


/** Make everything,
 * a drawing surface and some controls. */

  public void init () {

/* A big central drawing area and some controls on the edges */

     setLayout (new BorderLayout ());

     add ("Center", dPad = new DrawingPad2 ());

      dPad.setBackground (Color.white);
      dPad.setForeground (Color.blue);

     Panel np = new Panel ();
     np.setLayout (new GridLayout (0, 1));

     np.add (clearButton = new Button ("Clear"));

     np.add (drawButton = new Checkbox ("draw"));
     drawButton.setCheckboxGroup (modesGroup);

     np.add (lineButton = new Checkbox ("line"));
     lineButton.setCheckboxGroup (modesGroup);

     np.add (ovalButton = new Checkbox ("oval"));
     ovalButton.setCheckboxGroup (modesGroup);

     np.add (rectangleButton = new Checkbox ("rectangle"));
     rectangleButton.setCheckboxGroup (modesGroup);

/* Note set/getCurrent deprecated now set/getSelectedCheckbox */

     modesGroup.setCurrent (drawButton);

     add ("West", np);

     Panel sp = new Panel ();
     sp.setLayout (new GridLayout (0, 1));

     sp.add (biggerButton = new Button ("bigger"));
     sp.add (smallerButton = new Button ("smaller"));

     sp.add (writeHButton = new Checkbox ("Write Horiz"));
     writeHButton.setCheckboxGroup (writeHVGroup);

     sp.add (writeVButton = new Checkbox ("Write Vert"));
     writeVButton.setCheckboxGroup (writeHVGroup);

     writeHVGroup.setCurrent (writeHButton);

     sp.add (yellowButton = new Checkbox ("yellow"));
     yellowButton.setCheckboxGroup (colorsGroup);

     sp.add (redButton = new Checkbox ("red"));
     redButton.setCheckboxGroup (colorsGroup);

     sp.add (greenButton = new Checkbox ("green"));
     greenButton.setCheckboxGroup (colorsGroup);

     sp.add (blueButton = new Checkbox ("blue"));
     blueButton.setCheckboxGroup (colorsGroup);

     colorsGroup.setCurrent (blueButton);

     add ("East", sp);
  }

/** Handle the button and say we did */

  public boolean action (Event e, Object o) {
     if (e.target == clearButton) dPad.clear ();
     if (e.target == biggerButton) dPad.bigger ();
     if (e.target == smallerButton) dPad.smaller ();

     if ((e.target == redButton)
      && redButton.getState ()) dPad.setPaint (Color.red);
     if ((e.target == yellowButton)
      && yellowButton.getState ()) dPad.setPaint (Color.yellow);
     if ((e.target == greenButton)
      && greenButton.getState ()) dPad.setPaint (Color.green);
     if ((e.target == blueButton)
      && blueButton.getState ()) dPad.setPaint (Color.blue);

      if ((e.target == writeHButton)
       && writeHButton.getState ()) dPad.setHV (0);

      if ((e.target == writeVButton)
       && writeVButton.getState ()) dPad.setHV (1);

      if ((e.target == drawButton)
       && drawButton.getState ()) dPad.setMode (0);

      if ((e.target == lineButton)
       && lineButton.getState ()) dPad.setMode (1);

      if ((e.target == rectangleButton)
       && rectangleButton.getState ()) dPad.setMode (2);

      if ((e.target == ovalButton)
       && ovalButton.getState ()) dPad.setMode (3);

/* Must requestFocus to get key strokes */

     dPad.requestFocus ();

     return true;
  }

}

/** To run as an application,
 * construct it and show it in a frame.
 * This must be a static (class not instance) method,
 * because the instance won't exist until we construct it here.
 * When run as an applet,
 * the browser provides the frame,
 * and calls the constructor and init methods. */

  public static void main (String []argv) {

      Doodle2 cd = new Doodle2 ();

      Frame ff = new Frame ("Doodle2");
      ff.setBounds (100, 100, 600, 300);

      ff.add (cd);
      cd.init ();

      ff.show ();
  }

class DrawingPad2 extends Canvas {

  Image di = null;
  Graphics dg = null;
  int xpos, ypos, xold, yold;

  int mode = 0, hv = 0;

  DragShape2 dragShape = null;

  public void setPaint (Color fg) { dg.setColor (fg); }

  public void setMode (int m) { mode = m; }

  public void setHV (int m) { hv = m; }

/** Note position at mouse press,
 * what to do with it depends on drawing mode */

  public boolean mouseDown (Event e, int x, int y) {
     switch (mode) {

/* free hand drawing make sure no current shape */

     case 0:
	 dragShape = null; break;

/* start a draggable Line */

     case 1:
	 dragShape = new DragLine2 (x, y); break;

/* start a draggable Rect */

     case 2:
	 dragShape = new DragRect2 (x, y); break;

/* start a draggable Oval */

     case 3:
	 dragShape = new DragOval2 (x, y); break;
     }

     xold = x; yold = y;

     return true;
  }


  public boolean mouseDrag (Event e, int x, int y) {

/* Any current dragShape,
 * update it but do not draw it yet,
 * we do that in the paint method. */

     if (null != dragShape)
	 dragShape.setEnd (x, y);

/* Direct drawing mode,
 * add another line segment to the offscreen image. */

     else {
         xpos = x; ypos = y;
         if (dg != null)
             dg.drawLine (xold, yold, xpos, ypos);
         xold=xpos; yold=ypos;
     }

/* Either way we need a repaint */

     repaint ();
     return true;
  }

/* Any current dragShape,
 * update it one last time,
 * draw it into the offscreen image,
 * and get rid of it. */

  public boolean mouseUp (Event e, int x, int y) {
      if (null != dragShape) {
	  dragShape.setEnd (x, y);
	  dragShape.draw (dg);
	  dragShape = null;
          repaint ();
      }

      return true;
  }

/** Prevent pre-clear since we fill whole space anyway */

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

  public void paint (Graphics g) {
     if (di == null) {
         di = createImage (size ().width, size ().height);
         dg = di.getGraphics ();

         Color currentColor = dg.getColor ();
         dg.setColor (Color.black);
         dg.drawRect (0, 0, size ().width-1, size ().height-1);
         dg.setColor (currentColor);
     }

/* Everything from the accumulated offscreen image */

     g.drawImage (di, 0, 0, null);

/* Any current dragShape,
 * draw it directly onto screen in current color. */

     if (null != dragShape) {
         g.setColor (dg.getColor ());
	 dragShape.draw (g);
     }
  }

  public void clear () {
     if (null != dg) {
         dg.clearRect (0, 0, size ().width, size ().height);

         Color currentColor = dg.getColor ();
         dg.setColor (Color.black);
         dg.drawRect (0, 0, size ().width-1, size ().height-1);
         dg.setColor (currentColor);
     }
     repaint ();
  }

/* There is no keyClick just Down and Up.
 * "Keypress:C:NN"
 * C is String [1] for the key character if defined else "?".
 * NN is String [2 or more] for the numeric key code,
 * high numbers > 1000 are control keys. */

  public boolean keyDown (Event evt, int key) {
     if (dg == null) return false;

     if (key > 1000) return false;

 // System.out.println ("Keypress:"+String.valueOf ((char)key)+":"+String.valueOf (key));

     String ks = String.valueOf ((char)key);

     FontMetrics fm = dg.getFontMetrics ();

     dg.drawString (ks, xold, yold);

/* Advance either across or down */

     if (0 == hv)
         xold += fm.stringWidth (ks);
     else
         yold += fm.getHeight ();

     repaint ();
     return true;
  }

/** Must requestFocus to get key strokes */

  public synchronized boolean mouseEnter (Event me, int x, int y) {
     requestFocus ();
     return true;
  }

/** This **always** gives a newFont with getSize as requested,
 *  but they may be displayed as the same size anyway.
 *  and NOT change size on our Unix box,
 *  although fine on a Windows/NT box. */

  public void bigger () { changeFont (2); }

  public void smaller () { changeFont (-2); }

  void changeFont (int delta) {

     Component pcomp = getParent ();

     Font oldFont = getFont ();

     if ((null == oldFont)
      && (null != pcomp))
         oldFont = pcomp.getFont ();

     if (null == oldFont)
         return;

     Font newFont = new Font
          (oldFont.getName (),
           oldFont.getStyle (),
           oldFont.getSize () + delta);

/* Existing dg won't see it so tell them */

     setFont (newFont);
     dg.setFont (newFont);
   }

}

abstract class DragShape2 {

    int x = 0, y = 0,
       xx = 0, yy = 0;

    DragShape2 (int x, int y) {
	this.x = x;
	this.y = y;
    }

    public void setEnd (int xx, int yy) {
	this.xx = xx;
	this.yy = yy;
    }

    public abstract void draw (Graphics g) ;

}

class DragLine2 extends DragShape2 {

    DragLine2 (int x, int y) { super (x, y); }

    public void draw (Graphics g) {
        g.drawLine (x, y, xx, yy);
    }
}

class DragOval2 extends DragShape2 {

    DragOval2 (int x, int y) { super (x, y); }

// All this jugglery because we do not want to draw oval
// outside the applet area. Also we should be able to draw
// the oval even if our starting point is at bottom right
// and end point is at top left of the applet. Note that
// left to right is the positive x axis and top
// to bottom is the positive y axis.

    public void draw (Graphics g) {

        int
        minx = Math.min (x, xx),
        miny = Math.min (y, yy),
        maxx = Math.max (x, xx),
        maxy = Math.max (y, yy);

        g.drawOval (minx, miny, maxx-minx, maxy-miny);
    }
}

class DragRect2 extends DragShape2 {

    DragRect2 (int x, int y) { super (x, y); }

    public void draw (Graphics g) {

        int
        minx = Math.min (x, xx),
        miny = Math.min (y, yy),
        maxx = Math.max (x, xx),
        maxy = Math.max (y, yy);

        g.drawRect (minx, miny, maxx-minx, maxy-miny);
    }

}
