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

public class DrawA extends Applet
      implements ItemListener 
{
 public void init()  {
 setLayout(new BorderLayout());
  Panel p = new Panel();
  p.setLayout(new FlowLayout());
  CheckboxGroup g = new CheckboxGroup();
// add item listener to each of these checkboxes
  p.add(lin = new Checkbox("line",g,true));
lin.addItemListener(this);
  p.add(sqar = new Checkbox("rectangle",g,false));
sqar.addItemListener(this);
  p.add(oval = new Checkbox("oval",g,false));
oval.addItemListener(this);
  p.add(del = new Checkbox("delete",g,false));
del.addItemListener(this);
  p.add(mov = new Checkbox("move",g,false));
mov.addItemListener(this);

  add("North",p);
  draw_surface = new DrawC ();
  add("Center",draw_surface);
 } 
 
public void itemStateChanged(ItemEvent evt)
{ Object source = evt.getSource();
  if (source == lin)
       draw_surface.SetMode(0);
  else if(source == sqar)
       draw_surface.SetMode(1);
  else if(source == oval)
       draw_surface.SetMode(2);
  else if(source == del)
       draw_surface.SetMode(3);
  else if(source == mov)
       draw_surface.SetMode(4);
  }

 
private DrawC draw_surface;
private Checkbox lin;
private Checkbox sqar;
private Checkbox oval;
private Checkbox del;
private Checkbox mov;

}

class DrawC  extends Canvas
 implements MouseMotionListener
{
/* This no argument constructor is needed
   to initialize the array of objects which
   would otherwise contain null pointers..
   See By Example page 94 we also need 
   to add the mouseAdapter up here with the
   initialization.. Because we're going to use
   one of two mouseMotion events we'll just
   go ahead & dummy up mouseMotion ourselves.. 
*/

DrawC() { 
for (int i=0; i < mdra.length; i++) {
mdra[i] = new DraObj();
 } 
// } moving this below the definition of the mouse adapter
 

       
// enclose these in mouseAdapter

 addMouseListener (new MouseAdapter() 
{
public void mousePressed(MouseEvent evt)
{ 
 StartX = evt.getX();
   StartY = evt.getY();
   OldX = StartX;
   OldY = StartY;

if(dra_typ > 2) {
target = find(StartX,StartY);
if (target >= 0){
 if(dra_typ == 3) {
for (int i = target; i < nobs -1; i++)
/* mdra[i]=mdra[i+1]; */
{
buf.setObp(mdra[i+1].getTyp(), mdra[i+1].getX(), mdra[i+1].getY(), 
           mdra[i+1].getWidth(), mdra[i+1].getHeight());
mdra[i].setObp(buf.getTyp(), buf.getX(), buf.getY(), 
           buf.getWidth(), buf.getHeight());
 } 
nobs -= 1;
//this.update(this.getGraphics()); 
}
if(dra_typ == 4 && target >= 0) {
StartX = evt.getX() - mdra[target].getX();
StartY = evt.getY() - mdra[target].getY();
}} }}



   
public void mouseReleased(MouseEvent evt)
{  
// enter the drawn object in the array
switch(dra_typ) {
 case 0:
  if (StartX < OldX)
   mdra[nobs].setObp(dra_typ,StartX,StartY,OldX - StartX, OldY - StartY);
  else
   mdra[nobs].setObp(dra_typ,OldX,OldY,StartX - OldX, StartY - OldY);
break;
 case 1:
mdra[nobs].setObp(dra_typ,
 (StartX < OldX ? StartX : OldX), (StartY < OldY ? StartY : OldY),
 ((StartX > OldX ? StartX : OldX) - (StartX < OldX ? StartX : OldX)),
 ((StartY > OldY ? StartY : OldY) - (StartY < OldY ? StartY : OldY)));
break;        
 case 2:
mdra[nobs].setObp(dra_typ,
 (StartX < OldX ? StartX : OldX), (StartY < OldY ? StartY : OldY),
 ((StartX > OldX ? StartX : OldX) - (StartX < OldX ? StartX : OldX)),
 ((StartY > OldY ? StartY : OldY) - (StartY < OldY ? StartY : OldY)));
break;
 case 4:
if(target >= 0)
mdra[target].setPos(evt.getX() - StartX, evt.getY() - StartY);
break;
 default:
break;
}
if(dra_typ < 3)
nobs += 1; 
 }} );}

// End for Mouse Adapter





// add a dummy mouse moved for the interface

public void mouseMoved(MouseEvent evt){}


public void mouseDragged(MouseEvent evt)
{ 

// Draw out the old & draw in the new

Graphics g = getGraphics();
g.setXORMode(getBackground());

switch (dra_typ)
{ 
 case 0: 

// the line

 g.drawLine(StartX, StartY, OldX, OldY);
 OldX = evt.getX();
 OldY = evt.getY();
 g.drawLine(StartX, StartY, OldX, OldY);
  break;

 case 1:

// the rectangle

 g.drawRect( (StartX < OldX ? StartX : OldX), (StartY < OldY ? StartY : OldY),
 (StartX > OldX ? StartX : OldX) - (StartX < OldX ? StartX : OldX),
 (StartY > OldY ? StartY : OldY) - (StartY < OldY ? StartY : OldY));

 OldX = evt.getX();
 OldY = evt.getY();

 g.drawRect( (StartX < OldX ? StartX : OldX), (StartY < OldY ? StartY : OldY),
 (StartX > OldX ? StartX : OldX) - (StartX < OldX ? StartX : OldX),
 (StartY > OldY ? StartY : OldY) - (StartY < OldY ? StartY : OldY));
  break;

 case 2:

// the elipse

 g.drawOval( (StartX < OldX ? StartX : OldX), (StartY < OldY ? StartY : OldY),
 (StartX > OldX ? StartX : OldX) - (StartX < OldX ? StartX : OldX),
 (StartY > OldY ? StartY : OldY) - (StartY < OldY ? StartY : OldY));

 OldX = evt.getX();
 OldY = evt.getY();

 g.drawOval( (StartX < OldX ? StartX : OldX), (StartY < OldY ? StartY : OldY),
 (StartX > OldX ? StartX : OldX) - (StartX < OldX ? StartX : OldX),
 (StartY > OldY ? StartY : OldY) - (StartY < OldY ? StartY : OldY));
  break;
  
case 4:

// the move
if(target >= 0) {
 int mw = mdra[target].getWidth();
 int mh = mdra[target].getHeight();
switch (mdra[target].getTyp()) {
 case 0:
  g.drawLine(OldX - StartX, OldY - StartY, OldX - StartX + mw, OldY - StartY +mh);
 OldX = evt.getX();
 OldY = evt.getY();
  g.drawLine(OldX - StartX, OldY - StartY, OldX - StartX + mw, OldY - StartY +mh);                
  break;
 case 1:
  g.drawRect(OldX - StartX, OldY - StartY, mw, mh);
 OldX = evt.getX();
 OldY = evt.getY();
  g.drawRect(OldX - StartX, OldY - StartY, mw, mh);
  break;
 case 2:
  g.drawOval(OldX - StartX, OldY - StartY, mw, mh);
 OldX = evt.getX();
 OldY = evt.getY();
  g.drawOval(OldX - StartX, OldY - StartY, mw, mh);
    break;
 } }
 default:
 break;
}}

// The Non Event helper methods

public int find(int x, int y)
{
 for (int i= 0; i < nobs; i++){
 int tx = mdra[i].getX();
 int ty = mdra[i].getY();
 int tw = mdra[i].getWidth();
 int th = mdra[i].getHeight();
  if(mdra[i].getTyp() == 0)
   {
float testy = ((float)th/tw) * (x - tx);
  if ((testy > y - ty? testy : y - ty) - (testy < y - ty ? testy : y - ty) <= Tol)
   return i;}
   
  else
  if (x > tx && x < tx + tw && y >ty && y < ty + th)
   return i;
}
return -1;
}

public void paint(Graphics g) {
g.setXORMode(getBackground());

 for (int i= 0; i < nobs; i++){
 int tp = mdra[i].getTyp();
 int tx = mdra[i].getX();
 int ty = mdra[i].getY();
 int tw = mdra[i].getWidth();
 int th = mdra[i].getHeight();
 switch (tp)
{ 
 case 0: 

// the line

 g.drawLine(tx,ty,tx+tw,ty+th);
  break;

 case 1:

// the rectangle

 g.drawRect(tx,ty,tw,th);
  break;

 case 2:

// the elipse

 g.drawOval(tx,ty,tw,th);
  break;
 
 default:
 break;
}}}

public void SetMode(int mode)
{ this.dra_typ = mode;
}


 
private static final int maxob = 100;
private static final int Tol = 4;
private int target;
private int dra_typ = 0;
private int nobs = 0;
private DraObj[] mdra = new DraObj[maxob];
private DraObj buf = new DraObj();


 
private int StartX;
private int StartY;
private int OldX;
private int OldY; 
}
  




class DraObj extends Object

{

/*
 accessor methods
 We need access to all five
 fields so we can test for object
 selection for move & delete
*/

public int getTyp()
  { return dtyp;
  }

public int getX()
  { return startx;
  }

public int getY()
  { return starty;
  }

public int getWidth()
  { return width;
  }

public int getHeight()
  { return height;
  }

/* mutator methods
   We only need location
   for the move operation
*/


public void setObp(int typ, int x, int y, int w, int h)
  { this.dtyp = typ;
    this.startx = x;
    this.starty = y;    
    this.width = w;
    this.height = h;
  }
  
public void setPos(int x, int y)
 { this.startx = x;
   this.starty = y;
 }

private int dtyp; 
private int startx;
private int starty;
private int width;
private int height;
}