content: /*
   *    This program is free software; you can redistribute it and/or modify
   *    it under the terms of the GNU General Public License as published by
   *    the Free Software Foundation; either version 2 of the License, or
   *    (at your option) any later version.
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *    GNU General Public License for more details.
   *
   *    You should have received a copy of the GNU General Public License
   *    along with this program; if not, write to the Free Software
   *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  /*
   *    BeanInstance.java
   *    Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
   *
   */
  
  package weka.gui.beans;
  
  import java.awt.Dimension;
  import java.awt.Font;
  import java.awt.FontMetrics;
  import java.awt.Graphics;
  import java.awt.Point;
  import java.awt.Rectangle;
  import java.beans.Beans;
  import java.io.Serializable;
  import java.util.Vector;
  import java.util.ArrayList;
  
  import javax.swing.JComponent;
  
  /**
   * Class that manages a set of beans.
   *
   * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a>
   * @version  $Revision: 7155 $
   * @since 1.0
   */
  public class BeanInstance
    implements Serializable {
  
    /** for serialization */
    private static final long serialVersionUID = -7575653109025406342L;
  
    private static ArrayList<Vector> TABBED_COMPONENTS = 
      new ArrayList<Vector>();
    
  /*  static {
      Vector initial = new Vector();
      TABBED_COMPONENTS.add(initial);
    } */
    
    /**
     * Sets up just a single collection of bean instances in the first
     * element of the list. This is useful for clients that are using
     * XMLBeans to load beans.
     */
    public static void init() {
      TABBED_COMPONENTS.clear();
      TABBED_COMPONENTS.add(new Vector());
    }
    
    /**
     * class variable holding all the beans
     */
  //  private static Vector COMPONENTS = new Vector();
  
    public static final int IDLE = 0;
    public static final int BEAN_EXECUTING = 1;
    
    /**
     * Holds the bean encapsulated in this instance
     */
    private Object m_bean;
    private int m_x;
    private int m_y;
  
    /**
     * Removes all beans from containing component
     *
     * @param container a <code>JComponent</code> value
     */
    public static void removeAllBeansFromContainer(JComponent container, 
        Integer... tab) {
      
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
      
      if (container != null) {
        if (components != null) {
  	for (int i = 0; i < components.size(); i++) {
  	  BeanInstance tempInstance = (BeanInstance)components.elementAt(i);
  	  Object tempBean = tempInstance.getBean();
  	  if (Beans.isInstanceOf(tempBean, JComponent.class)) {
  	    container.remove((JComponent)tempBean);
  	  }
  	}
        }
        container.revalidate();
      }    
    }
  
    /**
     * Adds all beans to the supplied component
     *
     * @param container a <code>JComponent</code> value
     */
    public static void addAllBeansToContainer(JComponent container, Integer... tab) {
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
      
      if (container != null) {
        if (components != null) {
  	for (int i = 0; i < components.size(); i++) {
  	  BeanInstance tempInstance = (BeanInstance)components.elementAt(i);
  	  Object tempBean = tempInstance.getBean();
  	  if (Beans.isInstanceOf(tempBean, JComponent.class)) {
  	    container.add((JComponent)tempBean);
  	  }
  	}
        }
        container.revalidate();
      }
    }
  
    /**
     * Return the list of displayed beans
     * 
     * @param tab varargs parameter specifying the index of
     * the collection of beans to return - if omitted then
     * the first (i.e. primary) collection of beans is returned 
     *
     * @return a vector of beans
     */
    public static Vector getBeanInstances(Integer... tab) {
      Vector returnV = null;
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      if (TABBED_COMPONENTS.size() > 0) {
        returnV = TABBED_COMPONENTS.get(index);
      }
      
      return returnV;
    }
  
    /**
     * Adds the supplied collection of beans at the supplied index in
     * the list of collections. If the index is not supplied then
     * the primary collection is set (i.e. index 0). Also adds
     * the beans to the supplied JComponent container (if not null)
     *
     * @param beanInstances a <code>Vector</code> value
     * @param container a <code>JComponent</code> value
     */
    public static void setBeanInstances(Vector beanInstances, 
  				      JComponent container, 
  				      Integer... tab) {
      removeAllBeansFromContainer(container, tab);
      
      if (container != null) {
        for (int i = 0; i < beanInstances.size(); i++) {
  	Object bean = ((BeanInstance)beanInstances.elementAt(i)).getBean();
  	if (Beans.isInstanceOf(bean, JComponent.class)) {
  	  container.add((JComponent)bean);
  	}
        }
        container.revalidate();
        container.repaint();
      }
      
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      if (index < TABBED_COMPONENTS.size()) {
        TABBED_COMPONENTS.set(index, beanInstances);
      }
      
      // COMPONENTS = beanInstances;
    }
    
    /**
     * Adds the supplied collection of beans to the end of the list
     * of collections and to the JComponent container (if not null)
     * 
     * @param beanInstances the vector of bean instances to add
     * @param container
     */
    public static void addBeanInstances(Vector beanInstances, JComponent container) {
      // reset(container);
      
      if (container != null) {
        for (int i = 0; i < beanInstances.size(); i++) {
          Object bean = ((BeanInstance)beanInstances.elementAt(i)).getBean();
          if (Beans.isInstanceOf(bean, JComponent.class)) {
            container.add((JComponent)bean);
          }
        }
        container.revalidate();
        container.repaint();
      }
      
      TABBED_COMPONENTS.add(beanInstances);    
    }
    
    /**
     * Remove the vector of bean instances from the supplied index in
     * the list of collections.
     * 
     * @param tab the index of the vector of beans to remove.
     */
    public static void removeBeanInstances(JComponent container, Integer tab) {
      
      if (tab >= 0 && tab < TABBED_COMPONENTS.size()) {
        System.out.println("Removing vector of beans at index: " + tab);
        removeAllBeansFromContainer(container, tab);
        TABBED_COMPONENTS.remove(tab.intValue());
      }        
    }
  
    /**
     * Renders the textual labels for the beans.
     *
     * @param gx a <code>Graphics</code> object on which to render
     * the labels
     */
    public static void paintLabels(Graphics gx, Integer... tab) {
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
      
      if (components != null) {
        gx.setFont(new Font(null, Font.PLAIN, 9));
        FontMetrics fm = gx.getFontMetrics();
        int hf = fm.getAscent();
        for (int i = 0; i < components.size(); i++) {
          BeanInstance bi = (BeanInstance)components.elementAt(i);
          if (!(bi.getBean() instanceof Visible)) {
            continue;
          }
          int cx = bi.getX(); int cy = bi.getY();
          int width = ((JComponent)bi.getBean()).getWidth();
          int height = ((JComponent)bi.getBean()).getHeight();
          String label = ((Visible)bi.getBean()).getVisual().getText();
          int labelwidth = fm.stringWidth(label);
          if (labelwidth < width) {
            gx.drawString(label, (cx+(width/2)) - (labelwidth / 2), cy+height+hf+2);
          } else {
            // split label
  
            // find mid point
            int mid = label.length() / 2;
            // look for split point closest to the mid
            int closest = label.length();
            int closestI = -1;
            for (int z = 0; z < label.length(); z++) {
              if (label.charAt(z) < 'a') {
                if (Math.abs(mid - z) < closest) {
                  closest = Math.abs(mid - z);
                  closestI = z;
                }
              }
            }
            if (closestI != -1) {
              String left = label.substring(0, closestI);
              String right = label.substring(closestI, label.length());
              if (left.length() > 1 && right.length() > 1) {
                gx.drawString(left, (cx+(width/2)) - (fm.stringWidth(left) / 2), 
                    cy+height+(hf * 1)+2);
                gx.drawString(right, (cx+(width/2)) - (fm.stringWidth(right) / 2), 
                    cy+height+(hf * 2)+2);
              } else {
                gx.drawString(label, (cx+(width/2)) - (fm.stringWidth(label) / 2), 
                    cy+height+(hf * 1)+2);
              }
            } else {
              gx.drawString(label, (cx+(width/2)) - (fm.stringWidth(label) / 2), 
                  cy+height+(hf * 1)+2);
            }
          }
        }
      }
    }
  
    /**
     * Looks for a bean (if any) whose bounds contain the supplied point
     *
     * @param p a point
     * @return a bean that contains the supplied point or null if no bean
     * contains the point
     */
    public static BeanInstance findInstance(Point p, Integer... tab) {
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
      
      Rectangle tempBounds = new Rectangle();
      for (int i=0; i < components.size(); i++) {
        
        BeanInstance t = (BeanInstance)components.elementAt(i);
        JComponent temp = (JComponent)t.getBean();
  				      
        tempBounds = temp.getBounds(tempBounds);
        if (tempBounds.contains(p)) {
  	return t;
        }
      }
      return null;
    }
  
    /**
     * Looks for all beans (if any) located within
     * the supplied bounding box. Also adjusts the
     * bounding box to be a tight fit around all
     * contained beans
     *
     * @param boundingBox the bounding rectangle
     * @return a Vector of BeanInstances
     */
    public static Vector findInstances(Rectangle boundingBox, Integer... tab) {
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
      
      Graphics gx = null;
      FontMetrics fm = null;
      
      int centerX, centerY;
      int startX, startY, endX, endY;
      startX = (int)boundingBox.getX();
      startY = (int)boundingBox.getY();
      endX = (int)boundingBox.getMaxX();
      endY = (int)boundingBox.getMaxY();
      int minX = Integer.MAX_VALUE;
      int minY = Integer.MAX_VALUE;
      int maxX = Integer.MIN_VALUE;
      int maxY = Integer.MIN_VALUE;
      Vector result = new Vector();
      for (int i = 0; i < components.size(); i++) {
        BeanInstance t = (BeanInstance)components.elementAt(i);
        centerX = t.getX() + (t.getWidth()/2);
        centerY = t.getY() + (t.getHeight()/2);
        if (boundingBox.contains(centerX, centerY)) {
  	result.addElement(t);
  
  
  	// adjust bounding box stuff
  //	int hf = 0;
  	if (gx == null) {
  	  gx = ((JComponent)t.getBean()).getGraphics();
  	  gx.setFont(new Font(null, Font.PLAIN, 9));
  	  fm = gx.getFontMetrics();
  //	  hf = fm.getAscent();
  	}
  	String label = "";
  	if (t.getBean() instanceof Visible) {
  	  label = ((Visible)t.getBean()).getVisual().getText();
  	}
  	int labelwidth = fm.stringWidth(label);
  	int heightMultiplier = (labelwidth > t.getWidth())
  	? 2
  	: 1;
  	/*if (label.length() == 0) {
  	  heightMultiplier = 0;
  	}*/
  	int brx = 0;
  	int blx = 0;
  	if (centerX - (labelwidth / 2) - 2 < t.getX()) {
  	  blx = (centerX - (labelwidth / 2) - 2);
  	  brx = centerX + (labelwidth / 2) + 2;
  	} else {
  	  blx = t.getX() - 2;
  	  brx = t.getX() + t.getWidth() + 2;
  	}
  
  	if (blx < minX) {
  	  minX = blx;
  	}
  	if (brx > maxX) {
  	  maxX = brx;
  	}
  	if (t.getY() - 2 < minY) {
  	  minY = t.getY() - 2;
  	}
  	if (t.getY() + t.getHeight() + 2 > maxY) {
  	  maxY = t.getY() + t.getHeight()  + 2;
  	}
        }
      }
      boundingBox.setBounds(minX, minY, maxX - minX, maxY - minY);
      
      return result;
    }  
  
    /**
     * Creates a new <code>BeanInstance</code> instance.
     *
     * @param container a <code>JComponent</code> to add the bean to
     * @param bean the bean to add
     * @param x the x coordinate of the bean
     * @param y the y coordinate of the bean
     */
    public BeanInstance(JComponent container, Object bean, int x, int y, Integer... tab) {
      m_bean = bean;
      m_x = x;
      m_y = y;
      addBean(container, tab);
    }
  
    /**
     * Creates a new <code>BeanInstance</code> instance given the fully
     * qualified name of the bean
     *
     * @param container a <code>JComponent</code> to add the bean to
     * @param beanName the fully qualified name of the bean
     * @param x the x coordinate of the bean
     * @param y th y coordinate of the bean
     */
    public BeanInstance(JComponent container, String beanName, int x, int y, Integer... tab) {
      m_x = x;
      m_y = y;
      
      // try and instantiate the named component
      try {
        m_bean = Beans.instantiate(null, beanName);
      } catch (Exception ex) {
        ex.printStackTrace();
        return;
      }
  
      addBean(container, tab);
    }
  
    /**
     * Remove this bean from the list of beans and from the containing component
     *
     * @param container the <code>JComponent</code> that holds the bean
     */
    public void removeBean(JComponent container, Integer... tab) {
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
      
      for (int i = 0; i < components.size(); i++) {
        if ((BeanInstance)components.elementAt(i) == this) {
  	System.out.println("Removing bean");
  	components.removeElementAt(i);
        }
      }
      if (container != null) {
        container.remove((JComponent)m_bean);
        container.revalidate();
        container.repaint();
      }
    }
    
    public static void appendBeans(JComponent container, Vector beans, int tab) {
      if (TABBED_COMPONENTS.size() > 0 && tab < TABBED_COMPONENTS.size()) {
        Vector components = TABBED_COMPONENTS.get(tab);
        //
        
        for (int i = 0; i < beans.size(); i++) {
          components.add(beans.get(i));
          if (container != null) {
            Object bean = ((BeanInstance)beans.elementAt(i)).getBean();
            if (Beans.isInstanceOf(bean, JComponent.class)) {
              container.add((JComponent)bean);
            }
          }        
        }
        
        if (container != null) {
          container.revalidate();
          container.repaint();
        }      
      }
    }
  
    /**
     * Adds this bean to the global list of beans and
     * to the supplied container. The constructor
     * calls this method, so a client should not need
     * to unless they have called removeBean and then
     * wish to have it added again.
     *
     * @param container the Component on which this
     * BeanInstance will be displayed
     */
    public void addBean(JComponent container, Integer... tab) {
      
      int index = 0;
      if (tab.length > 0) {
        index = tab[0].intValue();
      }
      
      Vector components = null;
      if (TABBED_COMPONENTS.size() > 0 && index < TABBED_COMPONENTS.size()) {
        components = TABBED_COMPONENTS.get(index);
      }
  
      // do nothing if we are already in the list
      if (components.contains(this)) {
        return;
      }
  
      // Ignore invisible components
      if (!Beans.isInstanceOf(m_bean, JComponent.class)) {
        System.out.println("Component is invisible!");
        return;
      }
      
      components.addElement(this);
      
      // Position and layout the component
      JComponent c = (JComponent)m_bean;
      Dimension d = c.getPreferredSize();
      int dx = (int)(d.getWidth() / 2);
      int dy = (int)(d.getHeight() / 2);
      m_x -= dx;
      m_y -= dy;
      c.setLocation(m_x, m_y);
      //    c.doLayout();
      c.validate();
      //    bp.addBean(c);
      //    c.repaint();
      if (container != null) {
        container.add(c);
        container.revalidate();
      }
    }
  
    /**
     * Gets the bean encapsulated in this instance
     *
     * @return an <code>Object</code> value
     */
    public Object getBean() {
      return m_bean;
    }
  
    /**
     * Gets the x coordinate of this bean
     *
     * @return an <code>int</code> value
     */
    public int getX() {
      return m_x;
    }
  
    /**
     * Gets the y coordinate of this bean
     *
     * @return an <code>int</code> value
     */
    public int getY() {
      return m_y;
    }
  
    /**
     * Gets the width of this bean
     *
     * @return an <code>int</code> value
     */
    public int getWidth() {
      return ((JComponent)m_bean).getWidth();
    }
  
    /**
     * Gets the height of this bean
     *
     * @return an <code>int</code> value
     */
    public int getHeight() {
      return ((JComponent)m_bean).getHeight();
    }
   
    /**
     * Set the x and y coordinates of this bean
     *
     * @param newX the x coordinate
     * @param newY the y coordinate
     */
    public void setXY(int newX, int newY) {
      setX(newX);
      setY(newY);
      if (getBean() instanceof MetaBean) {
        ((MetaBean)getBean()).shiftBeans(this, false);
      }
    }
  
    /**
     * Sets the x coordinate of this bean
     *
     * @param newX an <code>int</code> value
     */
    public void setX(int newX) {
      m_x = newX;
      ((JComponent)m_bean).setLocation(m_x, m_y);
      ((JComponent)m_bean).validate();
    }
  
    /**
     * Sets the y coordinate of this bean
     *
     * @param newY an <code>int</code> value
     */
    public void setY(int newY) {
      m_y = newY;
      ((JComponent)m_bean).setLocation(m_x, m_y);
      ((JComponent)m_bean).validate();
    }
  }
  