import java.awt.*;
import java.applet.*;
import java.util.*;
import java.lang.Math;

import CharRender;
import Parse;

/****************************************************************/
/* Copyright 1996 The Regents of the University of Minnesota    */
/* All rights Reserved                                          */
/*                                                              */
/* Author: Robert R. Miner                                      */
/****************************************************************/

/****************************************************************/
/*  Box is the syntax tree class				*/
/*  Box() gets subclassed for each type of mathematical object	*/
/*  Each node contains the following data			*/
/*								*/
/*   (xpos, ypos)    	position point coordinates     		*/
/*   (hoffset, voffset)	coords of l.l. corner rel pos. pt	*/
/*   (height, width)    pretty obvious				*/
/*   (basex, basey)	position point to pass to children	*/
/*   			probably isn't necessary		*/
/*   scale              scale factor relative to parent		*/
/*  								*/
/*  There are a bunch of utility methods for managing the tree	*/
/*							       	*/
/*  size() recursively scans the tree and sets the size of	*/
/*    the component boxes working from the inside out		*/
/*							       	*/
/*  position() uses the computed box sizes to recursively	*/
/*    position them relative to each other			*/
/*							       	*/
/****************************************************************/

public class Box {

   int debug = 0;

   Box parent;
   Hashtable children;		//hash tables rule!!
   int nextchild = 0;		//number of children + 1
   Applet my_applet;

   public int height = 0;
   public int width = 0;
   public int hoffset = 0;
   public int voffset = 0;
   public int xpos = 0;
   public int ypos = 0;
   public int basex = 0;
   public int basey = 0;
   public int depth = 0;
   public int font = 0;
       
   public Box(Box myparent) {			//constructor for non-root boxes
      children = new Hashtable();
      parent = myparent;
      depth = myparent.depth;					
      font = myparent.font;
      my_applet= myparent.my_applet;
   }

   public Box(WebEQ app) { 	                //constructor for root box
      my_applet = app;					  
      children = new Hashtable();
      parent = null;
   }
   
   public void addChild(Box child) {
      children.put(new Integer(nextchild), child);
      ++nextchild;
   }

   public Box getChild(int n) {
      if (n < nextchild) {
	return (Box)children.get(new Integer(n));
      }
      else {
	return null;
      }
   }
   
   public Box prevChild(Box b) {
     Box me = b;
     Box pu = b.parent;
     int i;
     while (pu != null) {
       for ( i=0; i<pu.nextchild; i++) {
	 if (pu.getChild(i).equals(me)) {
	   if(i>0) {
	     return pu.getChild(i-1);
	   }
	   i = pu.nextchild;
	   break;
	 }
       }
       me = pu;
       pu = pu.parent;
     }
     return null;
   }

   public void setX(int x) {	xpos = x;   }

   public void setY(int y) {	ypos = y;   }

   public int widthIncrement() {   return width;   }

   public int heightIncrement() {   return 0;   }

   public String returnData() {  return this.getClass().getName();   }
 
   public void paint(Graphics g) {
      diagnostic(g, 0);

      for (int i = 0; i < nextchild; i++) {
	getChild(i).paint(g);
      }
   }
      
   public void diagnostic(Graphics g, int color) {
     switch (color) {
     case 1:
       g.setColor(Color.red);
       break;
     case 2:
       g.setColor(Color.green);
       break;
     case 3:
       g.setColor(Color.blue);
       break;
     case 4:
       g.setColor(Color.yellow);
       break;
     default:
       g.setColor(Color.black);
       break;
     }
     if (debug > 1) {
        System.out.println("nchld = " + new Integer(nextchild).toString());
	System.out.println("height = " + new Integer(height).toString());
 	System.out.println("width = " + new Integer(width).toString());
	System.out.println("hoffset = " + new Integer(hoffset).toString());
	System.out.println("voffset = " + new Integer(voffset).toString() );
	System.out.println("basex = " + new Integer(basex).toString());
	System.out.println("basey = " + new Integer(basey).toString() );
	System.out.println("depth = " + new Integer(depth).toString() );
	System.out.println("xpos = " + new Integer(xpos).toString() );
	System.out.println("ypos = " + new Integer(ypos).toString() );
  	System.out.println(this.returnData() + "\n");
      }
     if (debug > 0) {
       g.drawLine(xpos, ypos, xpos + width, ypos);
       g.drawLine(xpos + width, ypos, xpos + width, ypos - height);
       g.drawLine(xpos, ypos - height, xpos + width, ypos - height);
       g.drawLine(xpos, ypos, xpos, ypos - height);
     }
     g.setColor(Color.black);
    }


   public void size() {    		// computes the bounding box of
	Box tmp;		        // horizontal line of children
	int tmpvoff = 0;

	basex = hoffset;		// remember original offsets to
	basey = voffset;                // pass to chldn when positioning

	for (int i = 0; i < nextchild; i++) {
	   tmp = getChild(i);
	   tmp.size();
	   if (i == 0) {
	        height = tmp.height;
		tmpvoff = tmp.voffset;
	   }
	   if (tmpvoff < tmp.voffset) {
		height += tmp.voffset - tmpvoff;
	     	tmpvoff = tmp.voffset;
	   }
	   if (height - tmpvoff < tmp.height - tmp.voffset) {
	        height = tmp.height - tmp.voffset + tmpvoff;
	   }
	   width += tmp.width; 
	}
	voffset += tmpvoff;
   }

   public void stretchTo(int wh, int wv) { 
     Box tmp;

     for (int i = 0; i < nextchild; i++) {
       tmp = getChild(i);
       tmp.stretchTo(wh, wv);
       if (tmp.voffset > voffset) {
	 height += (tmp.voffset - voffset);
	 voffset = tmp.voffset;
       }
       if (tmp.height - tmp.voffset > height - voffset) {
	 height = voffset + tmp.height - tmp.voffset;
       }
     }
   }

   public void position() {
	Box tmp;
	int currentx = xpos;
	int currenty = ypos;
	
	//position itself using hoffset, voffset 
	xpos += hoffset; 
	ypos += voffset;

	//position children using basex, basey
   	for (int i = 0; i < nextchild; i++) {
	   tmp = getChild(i);
	   tmp.setX(currentx + basex);
	   tmp.setY(currenty + basey);;
	   tmp.position();
	   currentx += tmp.widthIncrement();
	   currenty += tmp.heightIncrement();
	}
    }
}

/*************************************************************/
/*  							     */
/* CharBox 	contains a single character	             */
/*		gets size and offset from render	     */
/*  							     */
/*************************************************************/

class CharBox extends Box {
    String data;
    Font fn;

    public CharBox(Box parentbox) {
	super(parentbox);		//install itself as a child
	depth = parentbox.depth;        //inherit its syntactic depth
    }

    public void addData(String s) {	//need to reflect true sizes
      data = s;
    }

    public void size() {	
      setfont(depth);
      FontMetrics fm = my_applet.getFontMetrics(fn);
      height = (int) (fm.getAscent() * multiplier());
      width = fm.charWidth(data.charAt(0));
    }
    
    public void stretchTo(int wh, int wv) {
      switch (data.charAt(0)) {
      case '|':
      case '(':
      case ')':
      case '[':
      case ']':
      case '{':
      case '}':
	voffset = wv;
	height = wh;
      }
    }  

    public void paint(Graphics g) {
      int ch = (int)data.charAt(0);
      char d = data.charAt(0); 

      diagnostic(g, 3);

      if (height != (int)(my_applet.getFontMetrics(fn).getAscent() * multiplier())) {
	switch (d) {
	case '|':
	  CharRender.paint(my_applet, g, CharRender.charVRUL, xpos, ypos, width, height);
	  break;
	case '(':
	  CharRender.paint(my_applet, g, CharRender.charLPAREN, xpos, ypos, width, height);
	  break;
	case ')':
	  CharRender.paint(my_applet, g, CharRender.charRPAREN, xpos, ypos, width, height);
	  break;
	case '[':
	  CharRender.paint(my_applet, g, CharRender.charLBRAK, xpos, ypos, width, height);
	  break;
	case ']':
	  CharRender.paint(my_applet, g, CharRender.charRBRAK, xpos, ypos, width, height);
	  break;
	case '{':
	  CharRender.paint(my_applet, g, CharRender.charLBRACE, xpos, ypos, width, height);
	  break;
	case '}':
	  CharRender.paint(my_applet, g, CharRender.charRBRACE, xpos, ypos, width, height);
	  break;
	}
      }
      else {
	CharRender.paint(g, fn, ch, xpos, ypos);  //character data
      }
    }

    void setfont(int depth) {

      if (debug > 0) {System.out.println("font="+font+" data="+data);}

      int std_pt = ((WebEQ)my_applet).standardPointsize(depth);

      switch (data.charAt(0)) {
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case '0':
      case '+':
      case '(':
      case ')':
      case '[':
      case ']':
      case '{':
      case '}':
      case '|':
      case '>':
      case '/':
      case ':':
	fn = new Font("TimesRoman", Font.PLAIN, std_pt);
	break;
      default:
	switch (font) {
	case 0:
	  fn = new Font("TimesRoman", Font.ITALIC, std_pt);
	  break;
	case 10:
	  fn = new Font("TimesRoman", Font.BOLD + Font.ITALIC, std_pt);
	  break;
	case 11:
	  fn = new Font("TimesRoman", Font.PLAIN, std_pt);
	  break;
	case 12:
	  fn = new Font("TimesRoman", Font.BOLD, std_pt);
	  break;
	}
	break;
      }
    }

    double multiplier() {
      switch ( data.charAt(0) ) {
      case 'a':
      case 'c':
      case 'e':
      case 'g':
      case 'm':
      case 'n':
      case 'o':
      case 'p':
      case 'q':
      case 'r':
      case 's':
      case 't':
      case 'u':
      case 'v':
      case 'w':
      case 'x':
      case 'y':
      case 'z':
	return .75;
      default:
	return 1;
      }
    }
      
}

/*************************************************************/
/*  							     */
/* EntityBox 	contains a single entity character           */
/*		gets size and offset from render	     */
/*  							     */
/*************************************************************/

class EntityBox extends Box {
 
   String data;
   int id;
 
   public EntityBox(Box parentbox) {  
	super(parentbox);   
   }

   public void addData(String s) {  data = s;   }

   public void size() {	    
     height = CharRender.getHeight(my_applet, id, depth);
     width = CharRender.getWidth(my_applet, id, depth);
     voffset = CharRender.getVoffset(my_applet, id, depth);
   }
   
   public void stretchTo(int wh, int wv) { 
     if (id == CharRender.charINT && (height-voffset) * 1.2 < wh -wv) {
       id = CharRender.charINTD;
       height = wh;
       voffset = wv;
     }
   }


   public void paint(Graphics g) {
     if (id > 2000) {
       CharRender.paint(my_applet, g, id, xpos, ypos, width, height);
       // drawn character data
     }
     else {
       CharRender.paint(my_applet, g, id, xpos, ypos, depth);
       // symbol font data
     }
     diagnostic(g, 4);
   }
}

/*************************************************************/
/*  							     */
/* SqrtBox 	sqrt wrapper box                             */
/*		gets size and offset from children	     */
/*  							     */
/*************************************************************/

class SqrtBox extends Box {

   public SqrtBox(Box parentbox) {
	super(parentbox);
   }

   public void size() {   

	//find BBox dimensions
	super.size();			

	height = (int)(height * 1.1);        //increase height
	width  += 18;         //increase width for surd

	//move over children boxes
	basex = 16;
   }

   public void paint(Graphics g) {

	CharRender.paint(my_applet, g, CharRender.charSQRT, xpos, ypos, width, height - 2);
	super.paint(g);	
    }
}


/*************************************************************/
/*  							     */
/* SubBox 	generic wrapper box, lowered by half height  */
/*		gets size and offset from children	     */
/*  							     */
/*************************************************************/

class SubBox extends Box {
   int align=0;

   public SubBox(Box parentbox, int algn) {
     super(parentbox);
     align = algn;
     depth = parentbox.depth + 1;
   }
   
   public void size() {   
     //find BBox dimensions
     super.size();			
     
     Box tmp = prevChild(this);
     
     if (tmp.getClass().getName().equals("SupBox")) {
       Box tmp2 = prevChild(tmp);                 //true ss target
	 
       basey = (int)(tmp2.voffset + height *.5  - voffset * .7 );
       voffset = basey + voffset;
       
       basex = -tmp.width;
       width = (width > tmp.width) ? width - tmp.width : 0;
     }
     else {
       basey = (int)(tmp.voffset + height *.5  - voffset * .7 );
       voffset = basey + voffset;
     }
   }

   public void truesize() {   
     //find BBox dimensions
     super.size();			
   }
}

/*************************************************************/
/*  							     */
/* SupBox 	generic wrapper box, raised by half height   */
/*		gets size and offset from children	     */
/*  							     */
/*************************************************************/

class SupBox extends Box {
  int align=0;
 
  public SupBox(Box parentbox, int algn) {
    super(parentbox);
    align=algn;
    depth = parentbox.depth + 1;
  }

  public void size() {   
    Box tmp = prevChild(this);
    
    //find BBox dimensions
      super.size();			
    
    //raise by half a line
      
      if (tmp.getClass().getName().equals("SubBox")) {
	Box tmp2 = prevChild(tmp);                 //true ss target
	  
	basey = - (int)((tmp2.height - tmp2.voffset) *.50 + voffset * .7);
	voffset = basey + voffset;
	
	basex = -tmp.width;
	width = (width > tmp.width) ? width - tmp.width : 0;
      }
      else {
	basey = - (int)((tmp.height - tmp.voffset) *.50 + voffset * .7);
	voffset = basey + voffset;
      }
  }

  public void truesize() {   
    //find BBox dimensions
    super.size();			
  }
}

/*************************************************************/
/*  							     */
/* BigOpBox 	BigOpBox -- symbol + [limits]                */
/*		gets size and offset from children	     */
/*  							     */
/*************************************************************/

class BigOpBox extends Box {

  public BigOpBox(Box parentbox) {   
    super(parentbox);  
  }
  
  public void size() {              // Take a deep breath ...

    EntityBox opr;
    Box limit;		       
    int loffset = 0;                //dist of op to left of BigOpBox
    int roffset = 0;                //dist of op to right of BigOpBox
    int lim_align = 0;
    
    basex = hoffset;		    // remember original offsets to
    basey = voffset;                // pass to chldn when positioning
        
    opr = (EntityBox)getChild(0);   //process the operator entity
    opr.size();
    height = opr.height;
    width = opr.width;
    voffset = opr.voffset;
    int baseht = height - voffset;
    
    for (int i = 1; i<nextchild; i++) {   //get limits

      limit = getChild(i);

      limit.basex = 0;                   /* to handle directly */

      if (limit.getClass().getName().equals("SubBox")) { 
	((SubBox)limit).truesize();			  
	lim_align = ((SubBox)limit).align;
	if (opr.id == CharRender.charINT) {           /* correct INT */
	  limit.basex = -(int)(opr.width * 0.4);
	}
      }
      else {					
	((SupBox)limit).truesize();			  
	lim_align = ((SupBox)limit).align;  
      }

      if (lim_align == 0) {              //alignment defaults
	switch (opr.id) {
	case CharRender.charSUM:
	case CharRender.charCAP:
	case CharRender.charCUP:
	case CharRender.charOPLUS:
	case CharRender.charOTIMES:
	  lim_align = 2;
	  break;
	case CharRender.charINT:
	default:
	  lim_align = 3;
	  break;
	}
      }

      switch (lim_align) {                // compute heights & voffsets
                                          // scan for loffset & roffset
      case 1:
	if (limit.width > loffset) {
	  width += (limit.width - loffset);
	  loffset = limit.width;
	}

        if (limit.getClass().getName().equals("SubBox")) {
	  limit.basey = opr.voffset + (int)(limit.height/2) - limit.voffset;
	  limit.voffset = limit.basey;
	  if (limit.voffset > voffset) {
	    voffset = limit.voffset;
	    height += limit.voffset - voffset;
	  }
	} 

        if (limit.getClass().getName().equals("SupBox")) {
	  limit.basey = opr.voffset-opr.height+(int)(limit.height/2)-limit.voffset;
	  limit.voffset = limit.basey;
	  if (limit.height - limit.voffset > height - voffset) {
	    height = voffset + limit.height - limit.voffset;
	  }
	} 
	break;

      case 2:
	if (limit.width > opr.width) {
	  width = limit.width;
	  loffset = (int)((limit.width - opr.width)/2);
          roffset = (int)((limit.width - opr.width)/2);
	}

        if (limit.getClass().getName().equals("SubBox")) {
	  limit.basey = opr.voffset + limit.height;
	  voffset += limit.height;
	  height += limit.height;
	} 

        if (limit.getClass().getName().equals("SupBox")) {
	  limit.basey = opr.voffset - opr.height;
	  height += limit.height;
	} 
	break;

      case 3:
	if (limit.width > roffset) {
	  width += (limit.width - roffset);
	  roffset = limit.width;
	}

        if (limit.getClass().getName().equals("SubBox")) {
	  limit.basey = opr.voffset + (int)(limit.height/2) - limit.voffset;
	  limit.voffset = limit.basey;
	  if (limit.voffset > voffset) {
	    height += limit.voffset - voffset;
	    voffset = limit.voffset;
	  }
	} 

        if (limit.getClass().getName().equals("SupBox")) {
	  limit.basey = opr.voffset-opr.height+(int)(limit.height/2)-limit.voffset;
	  limit.voffset = limit.basey;
	  if (limit.height - limit.voffset > height - voffset) {
	    height = voffset + limit.height - limit.voffset;
	  }
	} 
	break;
      }
    }	  

    opr.hoffset = loffset;

    for (int i = 1; i<nextchild; i++) {   // do hoffsets

      limit = getChild(i);

      if (limit.getClass().getName().equals("SubBox")) {
	lim_align = ((SubBox)limit).align;
      }
      else {					
	lim_align = ((SupBox)limit).align;  
      }

      if (lim_align == 0) {              //alignment defaults
	switch (opr.id) {
	case CharRender.charCAP:
	case CharRender.charCUP:
	case CharRender.charOPLUS:
	case CharRender.charOTIMES:
	case CharRender.charSUM:
	  lim_align = 2;
	  break;
	case CharRender.charINT:
	default:
	  lim_align = 3;
	  break;
	}
      }

      
      switch(lim_align) {
      case 1:
	limit.basex = opr.hoffset - limit.width;
	break;
      case 2:
	limit.basex = opr.hoffset + (int)( (opr.width - limit.width) / 2);
	break;
      case 3:
	limit.basex += opr.hoffset + opr.width;
	break;
      }
    }

    width = loffset + opr.width + roffset;
  }


  public void stretchTo(int ht, int voff) {

    ht = (int)(ht * 1.3);      /* adjust stretch for BigOps */

    int voff_inc = (voff > voffset) ? voff - voffset : 0;
    int height_inc = (ht - voff > height - voffset) ? ht - voff + voffset - height : 0;
    
    Box tmp = getChild(0);                //try to resize the operator
    int th = tmp.height;
    int tv = tmp.voffset;
    tmp.stretchTo(tmp.height + height_inc, tmp.voffset + voff_inc);
    voff_inc = tmp.voffset - tv;
    height_inc = tmp.height - th;

    voffset += voff_inc;                  //Adjust BigOpBox size
    height += height_inc;
    
    for (int i = 1; i < nextchild; i++) {  //process limits
      tmp = getChild(i);
      if (tmp.voffset > 0) {                     //lower limit
	tmp.voffset += voff_inc;
        tmp.basey += voff_inc;
      }
      else {
	tmp.voffset -= (height_inc - voff_inc);  // upper limit
	tmp.basey -= (height_inc - voff_inc);
      }
    }
  }

  public void position() {
    Box tmp;
    int currentx = xpos;
    int currenty = ypos;
    
    xpos += hoffset;
    ypos += voffset;
    
    tmp = getChild(0);  //place operator
    tmp.setX(currentx + basex);
    tmp.setY(currenty + basey);;
    tmp.position();	

    for (int i = 1; i < nextchild; i++) { //limits if any
      tmp = getChild(i);
      tmp.setX(currentx + basex);
      tmp.setY(currenty + basey);;
      tmp.position();
    }
  }
   
  public void paint(Graphics g) {
    super.paint(g);
    diagnostic(g, 2);
  }
}

/*************************************************************/
/*  							     */
/* StretchBox 	implements <LEFT> and <RIGHT> tags           */
/*		propagates a stetchTo to children	     */
/*  							     */
/*************************************************************/

class StretchBox extends Box {
  int left, right, mid, midop;
  double stretchfactor=1.0;

  public StretchBox(Box parentbox) {
    super(parentbox);
    left = 0;
    right = 0;
    mid = 99;
    midop = 0;
  }

  public void size() {
    Box tmp, tmpN, tmpD;
    int td=0, tn=0;
    int pointsize = ((WebEQ)my_applet).standardPointsize(depth);

    basex = hoffset;		    // remember original offsets to
    basey = voffset;                // pass to chldn when positioning
    
    if (midop == 0) {
      tmp = getChild(left);
      tmp.size();
      height = tmp.height;
      width = tmp.width;
      voffset = tmp.voffset;
    }
    else {
      tmpN = getChild(left);
      tmpN.size();
      width = tmpN.width;
      tmpD = getChild(left + 1);
      tmpD.size();

      //Center the narrower of the numerator and denominator

      if (tmpD.width > width) {                
	tmpN.hoffset = (int)((tmpD.width - width) / 2);
	tmpN.basex = tmpN.hoffset;
	width = tmpD.width; 
      }
      else {
	tmpD.hoffset = (int)((width - tmpD.width) / 2);
	tmpD.basex = tmpD.hoffset;
      }
      
      // expand height in case of <OVER>, <CHOOSE>

      height = tmpN.height + tmpD.height;
      if (midop == 5 | midop == 7) {           
	height += (int)(0.25 * pointsize); 
      }
      
      // adjust width

      width += (int)(.44 * pointsize);
      basex += (int)(.20 * pointsize);

      // shift numerator and denominator boxes into place
      // and manually set basex and basey 

      td = tmpD.height - (int)((pointsize * Math.pow(.24, depth + 1.0)) );     
      tn = td + tmpN.height - height ;

      tmpD.basey = td - tmpD.voffset;
      tmpN.basey = tn - tmpN.voffset;

      tmpD.voffset = td;
      tmpN.voffset = tn;

      voffset = tmpD.voffset;
    }

    // process the left term if present

    if (left == 1) {
      tmp = getChild(0);
      tmp.size();

      /* propagate stretch to delimiters */
      int sht = (int)(height * stretchfactor);
      int svo = (int)(voffset + height * (stretchfactor - 1) / 2);
      tmp.stretchTo(sht, svo);

      if (tmp.height > height) {
	height = tmp.height;
      }
      if (tmp.voffset > voffset) {
	voffset = tmp.voffset; 
      } 
      width += tmp.width;
    }

    // process the right term if present

    if (right != 0) {
      tmp = getChild(nextchild - 1);
      tmp.size();

      /* propagate stretch to delimiters */
      int sht = height;
      int svo = voffset;
      if (left != 1) {
	sht = (int)(height * stretchfactor);
	svo = (int)(voffset + height * (stretchfactor - 1)/2);
      }
      tmp.stretchTo(sht, svo);

      if (tmp.height - tmp.voffset > height - voffset) {
	height = tmp.height - tmp.voffset + voffset;
      }
      if (tmp.voffset > voffset) {
	voffset = tmp.voffset; 
      }
      width += tmp.width;
    }	
  }

  public void position() {
    Box tmp;
    int currentx = xpos;
    int currenty = ypos;
    int wid_inc = 0, wid_tmp = 0;
    
    xpos += hoffset;
    ypos += voffset;

    for (int i = 0; i < nextchild; i++) {
      tmp = getChild(i);
      tmp.setX(currentx + basex);
      tmp.setY(currenty + basey);;
      tmp.position();
      
      // advance currentx only after num and denom have been placed

      wid_inc = tmp.widthIncrement();
      if (i == mid-1) {                  
	wid_tmp = wid_inc;
	wid_inc = 0;
      }
      if (i == mid) {
	if (wid_inc < wid_tmp) { wid_inc = wid_tmp; }
      }
      currentx += wid_inc;
      currenty += tmp.heightIncrement();
    }
  }

  public void paint(Graphics g) {
    diagnostic(g, 1);
    Box tmp;
    int xt = 0, yt= 0,wt= 0;
    
    for (int i = 0; i < nextchild; i++) {
      tmp = getChild(i);
      tmp.paint(g);
      if (i == mid - 1) {
	xt = tmp.xpos;
	yt = tmp.ypos;
	wt = tmp.width;
      }
      if (i == mid) {
        //leaves room for descender in num
	yt = (int)((tmp.ypos - tmp.height - yt) * .75 + yt ); 
	if (tmp.width > wt) {wt = tmp.width;}               
	if (tmp.xpos < xt) {xt = tmp.xpos; }
      }
    }
    if (midop == 5) { 
      g.drawLine(xt - 2 , yt, xt + wt + 2, yt); 
      g.drawLine(xt - 2, yt + 1, xt + wt + 2, yt + 1); 
    }
  }
}


/*************************************************************/
/*  							     */
/* Font Box 	generic wrapper box, to propagate font       */
/*		specification to children           	     */
/*  							     */
/*************************************************************/

class FontBox extends Box {

  public FontBox(Box parentbox, int id) {
    super(parentbox);
    depth = parentbox.depth;
    font = id;
  }
}

/*************************************************************/
/*  							     */
/* Above Box 	contains 1 or 2 chilren, a target child      */
/*		and possilby a text child.           	     */
/*  							     */
/*************************************************************/

class AboveBox extends Box {
  int symbol;

  public AboveBox(Box parentbox, int sym) {
    super(parentbox);
    depth = parentbox.depth;
    symbol = sym;
  }

  public void size() {
    Box tmpTarget, tmpText;

    basex = hoffset;		    // remember original offsets to
    basey = voffset;                // pass to chldn when positioning
    
    tmpTarget = getChild(0);        // process Target
    tmpTarget.size();
    height = tmpTarget.height;
    width = tmpTarget.width;
    voffset = tmpTarget.voffset;
 
    // add sym height -- note getHeight param overloading

    height += CharRender.getHeight(my_applet, symbol, width);  

    if (nextchild == 2) {           /* process Text if present */
      tmpText = getChild(1);
      tmpText.size();
      tmpText.basey = voffset - height;

      height += (tmpText.height - tmpText.voffset);

      /*Center the narrower of the Text and the Target */

      if (tmpText.width > width) {                
	tmpTarget.hoffset = (int)((tmpText.width - width) / 2);
	tmpTarget.basex = tmpTarget.hoffset;
	width = tmpText.width; 
      }
      else {
	tmpText.hoffset = (int)((width - tmpText.width) / 2);
	tmpText.basex = tmpText.hoffset;
      }
    }
  }

  public void position() {
    Box tmp;
    int currentx = xpos;
    int currenty = ypos;
    
    xpos += hoffset;
    ypos += voffset;

    for (int i = 0; i < nextchild; i++) { /* don't advance currentx */
      tmp = getChild(i);
      tmp.setX(currentx + basex);
      tmp.setY(currenty + basey);
      tmp.position();
    }
  }

  public void paint(Graphics g) {
    Box tmp = getChild(0);
    int sh = CharRender.getHeight(my_applet, symbol, tmp.width);      

    int xp = xpos + tmp.hoffset;
    int yp = ypos - tmp.height;

    CharRender.paint(my_applet, g, symbol, xp, yp, tmp.width, sh);
    super.paint(g);	
  }
}

/*************************************************************/
/*  							     */
/* Array Box 	contains only rows                           */
/*  							     */
/*************************************************************/


class ArrayBox extends Box {
  int align;
  int columns = 0;
  short[] colwidth = new short[20];
  short[] colalign = new short[20];
  String[] colsep    = new String[20];
  short[] rowheight = new short [20];
  char ldelim=0;
  char rdelim=0;
  CharBox ldbox = null, rdbox = null;

  public ArrayBox(Box parentbox) {
    super(parentbox);
    for(int i = 0; i < 20; i++) {
      colsep[i] =" ";
    }
  }

  public void size() {
    Box tmp;
    RowBox row;
    int longest = 0;

    if (ldelim != 0) {
      ldbox = new CharBox(this);
      ldbox.addData(String.valueOf(ldelim));
      ldbox.size();
    }
    if (rdelim != 0) {
      rdbox = new CharBox(this);
      rdbox.addData(String.valueOf(rdelim));
      rdbox.size();
    }


    for (int i = 0; i < nextchild; i++) {  /* size rows & scan column widths */
      row = (RowBox)getChild(i);
      row.size();
      rowheight[i] = (short)(row.height * 1.2);
      if (row.nextchild > columns) {
	columns = row.nextchild;
	longest =i;
      }
      for (int j=0; j < row.nextchild; j++) {
	if (row.colwidth[j] > colwidth[j]) colwidth[j] = row.colwidth[j];
      }
      height += rowheight[i];             /* do height */
    }
    
    row = (RowBox)getChild(longest);     /* do width */
    for (int i = 0; i< columns; i++) {
      width += (colwidth[i] + row.sep[i].width);
    }
    if (ldelim !=0) width += ldbox.width;
    if (rdelim !=0) width += rdbox.width;

    switch (align) {                      /* set vertical alignment */
    case 1: 
      tmp = prevChild(this);
      voffset = height - tmp.height;
      break;
    case -1:
      voffset = 0;
      break;
    default:
      tmp = prevChild(this);
      int th = (tmp == null) ? 0 : tmp.height;
      voffset = (int)((height - th) * .5);
    }

    int vt = voffset;                     /* do  row offsets */
    for (int i = nextchild - 1; i >= 0; i--) {
      row = (RowBox)getChild(i);
      row.basey = vt - row.voffset;
      row.basex = (ldelim == 0) ? 0 : ldbox.width;
      row.voffset = vt;
      vt -= rowheight[i];
      row.width = width;                 /* for pretty debugging output */
      row.hoffset = (ldelim == 0) ? 0 : ldbox.width;
    }
    if (ldelim != 0) ldbox.stretchTo(height, voffset);
    if (rdelim != 0) {
      rdbox.stretchTo(height, voffset);
      rdbox.hoffset = width - rdbox.width;
      rdbox.basex = width - rdbox.width;
    }
  }

  public void position() {
    Box tmp;
    int currentx = xpos;
    int currenty = ypos;
    
    xpos += hoffset;
    ypos += voffset;

    for (int i = 0; i < nextchild; i++) { /* don't advance currentx */
      tmp = getChild(i);
      tmp.setX(currentx + basex);
      tmp.setY(currenty + basey);
      tmp.position();
    }
    if (rdelim != 0) {
      rdbox.setX(currentx);
      rdbox.setY(currenty);
      rdbox.position();
    }
    if (ldelim != 0) {
      ldbox.setX(currentx);
      ldbox.setY(currenty);
      ldbox.position();
    }
  }

  public void paint(Graphics g) {
    super.paint(g);
    if (rdelim != 0) rdbox.paint(g);
    if (ldelim != 0) ldbox.paint(g);
  }
}

class RowBox extends Box {
  short[] colwidth = new short[20];
  CharBox[] sep = new CharBox[20];
  Box item;

  public RowBox(Box parentbox) {
    super(parentbox);
  }

  public void size() {
    ArrayBox p = (ArrayBox)parent;

    for (int i = 0; i < nextchild; i++) {
      sep[i] = new CharBox(p);
      sep[i].addData(p.colsep[i]);
      sep[i].size();
      item = getChild(i);
      item.size();
      colwidth[i] = (short)(item.width); 
      if (item.height > height) height = item.height;
      if (item.voffset > voffset) voffset = item.voffset;
    }
    
    width = colwidth[0];
    sep[0].width = 0;
    for (int i = 1; i< nextchild; i++) {    //probably not necessary
      width += (sep[i].width + colwidth[i]);
    }
  }

  public void position() {
    Box tmp;

    int currentx = xpos;
    int currenty = ypos;
    int celloffset = 0;

    xpos += hoffset;
    ypos += voffset;

    for (int i = 0; i < nextchild; i++) {
      tmp = getChild(i);
      switch (((ArrayBox)parent).colalign[i]) {
      case 0:
	celloffset = (int)( ( ((ArrayBox)parent).colwidth[i] - tmp.width ) / 2);
	break;
      case 1:
	celloffset = (int)( ((ArrayBox)parent).colwidth[i] - tmp.width);
	break;
      default:
	celloffset=0;
      }
      sep[i].setX(currentx + basex);
      sep[i].setY(currenty + basey);
      sep[i].position();
      tmp.setX(currentx + basex + sep[i].width + celloffset );
      tmp.setY(currenty + basey);
      tmp.position();
      currentx += (int)( sep[i].width + ((ArrayBox)parent).colwidth[i] );
    }
  }

  public void paint(Graphics g) {
    super.paint(g);
    for (int i = 1; i < nextchild; i++) {
      sep[i].paint(g);
    }
  }
}










