

//=================================== PropertiesLayout ================================

import java.awt.*;
import java.util.*;

/**
 * PropertiesLayout is a LayoutManager that layout components in term of the location
 * & dimension of the other components. In PropertiesLayout, each component has a list
 * of properties, such as width, height, it's x center, etc. When you add a component
 * to a container that use this layout, you include a String that specifies these
 * properties (some are specified explicitly and some implicitly, as will be cleared
 * shortly). One of the important properties you can assign to a component, is it's
 * <i>name</i>. If you allready added a component and named it, say button1, now when
 * you add a new component (say label1) and want to specify it's width property, you can
 * <ol>
 * <li>give it a fixed width - "... width=$40 ..." (40 pixels).
 * <li>set it's width relative to the width of the container - ".. width=0.2 .." .
 * <li>give it the same width of another component - ".. width=button1.width ..".
 * </ol>
 * Here is a list of the properties we can use for a component:
 * <ul>
 * <li> name - a name to reference the component when placing the other components
 * <li> left - the x coordinate of the left edge of the component.
 * <li> top - the y coordinate of the top edge of the component.
 * <li> right, bottom - like left, top.
 * <li> x,y - the coordinates of the center of the component.
 * <li> width,height - the dimensions of the component.
 * <li> p - the margins to the left and right of the component (i'll give it a better name).
 * <li> s - margins to the top and bottom of the component.
 * <li> q - vertical space added to the 'top'.
 * </ul>
 * When you add a component you include a string of assignments <property>=<value>, the property
 * can be each one of the above, and the value can be enter with the 3 forms described before.
 * For example:
 * <pre>
 * add("name=button1,x=0.5,top=$10,width=button2.width",new Button("ok"));
 * </pre>
 * The above line adds a new button to the container, which will be placed 10 pixels from the
 * top of the container (top=$10), horizontaly centered (x=0.5), with width similar to that of
 * button2 defined before (width=button2.width), and with height as defined in the preferedSize()
 * method of the button.
 * <br>
 * Documentation is yet to be completed...
 *
 * @version 	1.01, 1/96
 * @author	Shmulik London
 * Feel free to change/improve the code. If you make real improvement of this class,
 * please document it and send it to me (suggestion for improvement: topological sort the
 * component added by dependencies, enable operations/formulas in the string specifying
 * the component properties, make it robust and independent of properties order (it is partialy
 * both).
 **/
public class PropertiesLayout implements LayoutManager {
   Hashtable components = null;

   public PropertiesLayout() {
      components = new Hashtable();
   }

   public Dimension preferredLayoutSize(Container parent) {
      Insets insets = parent.insets();
      return new Dimension(insets.left+insets.right+parent.size().width,
                           insets.top+insets.bottom+parent.size().height);
   }

   public Dimension minimumLayoutSize(Container parent) {
      return preferredLayoutSize(parent);
   }

   public void removeLayoutComponent(Component comp) {
   }

   /**
    * adds a component to the container, with a string that specifies
	* it's properties.
	*/
   public void addLayoutComponent(String how,Component comp) {
      LayingInfo info = new LayingInfo(how);
      components.put(comp,info);
      Object name = info.properties.get("n");
      if (name!=null)
         components.put(name,comp);
   }

   public void layoutContainer(Container parent) {
      int n = parent.countComponents();

      for (int i=0; i<n; i++) {
         Component comp = parent.getComponent(i);
         placeComponent(comp,parent);
         placeComponent(comp,parent);
      }
   }

   void placeComponent(Component comp,Container parent) {
      Insets insets = parent.insets();
      int w = parent.size().width;
      int h = parent.size().height;
        LayingInfo info = (LayingInfo)components.get(comp);
         int xx=insets.left;
         int yy=insets.top;
         int ww=comp.preferredSize().width;
         int hh=comp.preferredSize().height;
         Properties p = info.properties;
         String ref;
         if ((ref=p.getProperty("w"))!=null)
            ww=getCordinates(ref,w);
         if ((ref=p.getProperty("e"))!=null)
            ww+=getCordinates(ref,ww);
         if ((ref=p.getProperty("h"))!=null)
            hh=getCordinates(ref,h);
         if ((ref=p.getProperty("l"))!=null)
            xx=getCordinates(ref,w);
         if ((ref=p.getProperty("t"))!=null)
            yy=getCordinates(ref,h);
         if ((ref=p.getProperty("x"))!=null)
            xx=getCordinates(ref,w)-ww/2;
         if ((ref=p.getProperty("y"))!=null)
            yy=getCordinates(ref,h)-hh/2;
         if ((ref=p.getProperty("r"))!=null) {
            if (xx==0)
               xx=getCordinates(ref,w)-ww;
            else
               ww=getCordinates(ref,w)-xx;
         }
         if ((ref=p.getProperty("b"))!=null) {
            if (yy==0)
               yy=getCordinates(ref,h)-hh;
            else
               hh=getCordinates(ref,h)-yy;
         }
         if ((ref=p.getProperty("p"))!=null) {
            int dx=getCordinates(ref,w);
            ww-=2*dx;
            xx+=dx;
         }
         if ((ref=p.getProperty("s"))!=null) {
            int dy=getCordinates(ref,h);
            hh-=2*dy;
            yy+=dy;
         }
         if ((ref=p.getProperty("q"))!=null) {
            int dy=getCordinates(ref,w);
            yy+=dy;
         }
         comp.reshape(xx,yy,ww,hh);
   }

   private int getCordinates(String ref, int range) {
      try {
         if (ref.charAt(0)=='$')
            return Integer.valueOf(ref.substring(1)).intValue();
         if (Character.isDigit(ref.charAt(0)) || ref.charAt(0)=='.')
            return ((int)(Double.valueOf(ref).doubleValue()*range));
         // ref represents a property of another component, e.g. ref="button1.xm"
         StringTokenizer st = new StringTokenizer(ref,".");
         String name = st.nextToken();
         String prop = st.nextToken();
         Component c = (Component) components.get(name);
         switch (prop.charAt(0)) {
         case 't' : // top
            return c.location().y;
         case 'l' : // left
            return c.location().x;
         case 'w' : // width
            return c.size().width;
         case 'h' : // height
            return c.size().height;
         case 'r' : // right
            return c.location().x+c.size().width;
         case 'b' : // bottom
            return c.location().y+c.size().height;
         case 'x' : // xm - x center
            return c.location().x+(c.size().width/2);
         case 'y' : // ym = y center
            return c.location().y+(c.size().height/2);
         default :
            return 0;
         }
      } catch (Exception e) {
         return 0;
      }
   }
}

class LayingInfo {
   public Properties properties=new Properties();

   public LayingInfo(String info) {
      StringTokenizer st = new StringTokenizer(info,", =",true);
      boolean lookingForArg = true;
      String arg=null;
      while (st.hasMoreElements()) {
         String s = (String) st.nextElement();
         switch(s.charAt(0)) {
         case '=':
            lookingForArg=false;
            continue;
         case ',':
         case ' ':
         case '\t':
            continue;
         default:
         }
         if (lookingForArg) {
            arg=s.substring(0,1);
         } else {
            properties.put(arg,s);
            lookingForArg=true;
         }
      }
   }
}


