Table Layout : Customized Layout « Swing JFC « Java






Table Layout

    

/**
 * The utillib library.
 * More information is available at http://www.jinchess.com/.
 * Copyright (C) 2002, 2003 Alexander Maryanovsky.
 * All rights reserved.
 *
 * The utillib library is free software; you can redistribute
 * it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * The utillib library 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 Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with utillib library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;


/**
 * A LayoutManager which lays out the components in a table-like structure.
 * Unlike <code>GridLayout</code>, the sizes of the rows and columns
 * are dynamic, although properly aligned. The cell sizes are determined
 * according to the preferred sizes of the components and each component is
 * sized to either its maximum size or the cell size. Components are positioned
 * within their cells according to their X and Y alignments.
 * When a new component is added, it is placed in the first empty cell, in
 * lexigraphic order. A new row is created if necessary.
 * To create an empty cell, simply add blank component.
 */

public class TableLayout implements LayoutManager2{


  /**
   * The amount of columns in the table.
   */

  private final int columnCount;


  /**
   * The gap between columns, in pixels.
   */

  private final int xGap;


  /**
   * The gap between rows, in pixels.
   */

  private final int yGap;



  /**
   * A Vector of rows where each row is a Component array holding the components
   * in that row.
   */

  private final Vector rows = new Vector();



  /**
   * Creates a new TableLayout with the specified amount of columns,
   * horizontal/vertical gaps between columns/cells.
   */

  public TableLayout(int columnCount, int xGap, int yGap){
    if (columnCount <= 0)
      throw new IllegalArgumentException("The amount of columns must be positive");
    if (xGap < 0)
      throw new IllegalArgumentException("The horizontal gap may not be negative: "+xGap);
    if (yGap < 0)
      throw new IllegalArgumentException("The vertical gap may not be negative: "+yGap);

    this.columnCount = columnCount;
    this.xGap = xGap;
    this.yGap = yGap;
  }



  /**
   * Creates a new TableLayout with the specified amount of columns.
   */

  public TableLayout(int columnCount){
    this(columnCount, 0, 0);
  }




  /**
   * Adds the specified component to the layout.
   */

  public void addLayoutComponent(Component component, Object constraints){
    synchronized(component.getTreeLock()){
      int rowCount = rows.size();
      for (int i = 0; i < rowCount; i++){
        Component [] row = (Component [])rows.elementAt(i);
        for (int j = 0; j < row.length; j++){
          if (row[j] == null){
            row[j] = component;
            return;
          }
        }
      }

      Component [] newRow = new Component[columnCount];
      newRow[0] = component;
      rows.addElement(newRow);
    }
  }



  /**
   * Throws an exception.
   */

  public void addLayoutComponent(String name, Component component){
    throw new UnsupportedOperationException("deprecated addLayoutComponent(String, Component)");
  }




  /**
   * Removes the specified component from the layout.
   */

  public void removeLayoutComponent(Component component){
    synchronized(component.getTreeLock()){
      int rowCount = rows.size();
      outer: for (int i = 0; i < rowCount; i++){
        Component [] row = (Component [])rows.elementAt(i);
        for (int j = 0; j < row.length; j++){
          if (row[j] == component){
            row[j] = null;
            break outer;
          }
        }
      }

      // Remove any empty rows at the end.
      for (int i = rowCount - 1; i >= 0; i--){
        Component [] row = (Component [])rows.elementAt(i);
        boolean isEmpty = true;
        for (int j = 0; j < row.length; j++){
          if (row[j] != null){
            isEmpty = false;
            break;
          }
        }
        if (isEmpty)
          rows.removeElementAt(i);
        else
          break;
      }
    }
  }




  /**
   * Returns a matrix of Dimension objects specifying the preferred sizes of the
   * components we are going to layout.
   */

  private Dimension [][] getPreferredSizes(Container parent){
    int rowCount = rows.size();
    Dimension [][] prefSizes = new Dimension[rowCount][columnCount];

    for (int i = 0; i < rowCount; i++){
      Component [] row = (Component [])rows.elementAt(i);
      for (int j = 0; j < columnCount; j++){
        Component component = row[j];

        // Can only happen on the last line when all the remaining components are null as well
        if (component == null) 
          break;

        if (component.getParent() != parent)
          throw new IllegalStateException("Bad parent specified");

        prefSizes[i][j] = component.getPreferredSize();
      }
    }

    return prefSizes;
  }



  /**
   * Calculates and returns a Pair where the first object is an array holding
   * the column widths of our layout and the second is the rowHeights.
   */

  private Pair calculateLayout(Dimension [][] prefSizes){
    int rowCount = rows.size();

    int [] columnWidths = new int[columnCount];
    int [] rowHeights = new int[rowCount];

    // Find the maximum preferred row heights and column widths.
    for (int i = 0; i < rowCount; i++){
      for (int j = 0; j < columnCount; j++){
        Dimension prefSize = prefSizes[i][j];

        // Can only happen on the last line when all the remaining components are null as well
        if (prefSize == null)
          break;

        columnWidths[j] = Math.max(columnWidths[j], prefSize.width);
        rowHeights[i] = Math.max(rowHeights[i], prefSize.height);
      }
    }

    return new Pair(columnWidths, rowHeights);
  }




  /**
   * Lays out the specified container. Throws an
   * <code>IllegalStateException</code> if any of the components added via the
   * <code>addLayoutComponent</code> method have a different parent than the
   * specified Container.
   */

  public void layoutContainer(Container parent){
    synchronized(parent.getTreeLock()){
      int rowCount = rows.size();

      Insets parentInsets = parent.getInsets();

      // Collect the preferred sizes.
      Dimension [][] prefSizes = getPreferredSizes(parent);
      Pair layout = calculateLayout(prefSizes);
      int [] columnWidths = (int [])layout.getFirst();
      int [] rowHeights = (int [])layout.getSecond();

      Dimension prefParentSize = calculatePreferredLayoutSize(parent, columnWidths, rowHeights);
      Dimension parentSize = parent.getSize();
      Dimension layoutSize = 
        new Dimension(parentSize.width - xGap*(rowCount - 1) - parentInsets.left - parentInsets.right,
                      parentSize.height - yGap*(columnCount - 1) - parentInsets.top - parentInsets.bottom);
      Dimension prefLayoutSize =
        new Dimension(prefParentSize.width - xGap*(rowCount - 1) - parentInsets.left - parentInsets.right,
                      prefParentSize.height - yGap*(columnCount - 1) - parentInsets.top - parentInsets.bottom);

      // Layout the components.
      int y = parentInsets.top;
      for (int i = 0; i < rowCount; i++){
        int x = parentInsets.left;
        int cellHeight = (rowHeights[i]*layoutSize.height)/prefLayoutSize.height;
        Component [] row = (Component [])rows.elementAt(i);
        for (int j = 0; j < row.length; j++){
          int cellWidth = (columnWidths[j]*layoutSize.width)/prefLayoutSize.width;
          Component component = row[j];

          // Can only happen on the last line when all the remaining components are null as well
          if (component == null)
            break;

          Dimension maxSize = component.getMaximumSize();

          int compWidth = Math.min(maxSize.width, cellWidth);
          int compHeight = Math.min(maxSize.height, cellHeight);

          int compX = x + (int)((cellWidth - compWidth)*component.getAlignmentX());
          int compY = y + (int)((cellHeight - compHeight)*component.getAlignmentY());

          component.setBounds(compX, compY, compWidth, compHeight);

          x += cellWidth + xGap;
        }

        y += cellHeight + yGap;
      }
    }
  }



  /**
   * We're not caching anything yet, so this call is ignored.
   */

  public void invalidateLayout(Container parent){

  }



  /**
   * Returns the preferred layout for the specified parent container.
   */

  public Dimension preferredLayoutSize(Container parent){
    synchronized(parent.getTreeLock()){
      Dimension [][] prefSizes = getPreferredSizes(parent);
      Pair layout = calculateLayout(prefSizes);
      int [] columnWidths = (int [])layout.getFirst();
      int [] rowHeights = (int [])layout.getSecond();

      return calculatePreferredLayoutSize(parent, columnWidths, rowHeights);
    }
  }



  /**
   * Calculates the preferred layout size for the specified preferred column
   * widths and row heights.
   */

  private Dimension calculatePreferredLayoutSize(Container parent, int [] columnWidths, int [] rowHeights){
    int prefWidth = 0;
    int prefHeight = 0;

    for (int i = 0; i < columnWidths.length; i++)
      prefWidth += columnWidths[i];
    for (int i = 0; i < rowHeights.length; i++)
      prefHeight += rowHeights[i];

    // Add the gaps
    prefWidth += xGap*(columnWidths.length - 1);
    prefHeight += yGap*(rowHeights.length - 1);

    // Add parent insets
    Insets parentInsets = parent.getInsets();
    prefWidth += parentInsets.left + parentInsets.right;
    prefHeight += parentInsets.top + parentInsets.bottom;

    
    return new Dimension(prefWidth, prefHeight);
  }



  /**
   * Returns the same as <code>preferredLayoutSize</code>.
   */

  public Dimension minimumLayoutSize(Container parent){
    return preferredLayoutSize(parent);
  }




  /**
   * Returns a dimension with maximum possible values.
   */

  public Dimension maximumLayoutSize(Container parent){
    return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  }



  /**
   * Returns <code>CENTER_ALIGNMENT</code>;
   */

  public float getLayoutAlignmentX(Container parent) {
    return Component.CENTER_ALIGNMENT;
  }



  /**
   * Returns <code>CENTER_ALIGNMENT</code>;
   */

  public float getLayoutAlignmentY(Container parent) {
    return Component.CENTER_ALIGNMENT;
  }


}
/**
 * The utillib library.
 * More information is available at http://www.jinchess.com/.
 * Copyright (C) 2002 Alexander Maryanovsky.
 * All rights reserved.
 *
 * The utillib library is free software; you can redistribute
 * it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * The utillib library 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 Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with utillib library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */



/**
 * A wrapper for any two other given objects.
 */

 final class Pair{

  
  /**
   * The first object.
   */

  private final Object first;




  /**
   * The second object.
   */

  private final Object second;




  /**
   * Creates a new <code>Pair</code> with the two given objects. Either of the
   * objects may be <code>null</code>.
   */

  public Pair(Object first, Object second){
    this.first = first;
    this.second = second;
  }




  /**
   * Returns the first object.
   */

  public Object getFirst(){
    return first;
  }




  /**
   * Returns the second object.
   */

  public Object getSecond(){
    return second;
  }




  /**
   * Returns a hashcode combined from the hashcodes of the two target objects.
   */

  public int hashCode(){
    int hash1 = (first == null ? 0 : first.hashCode());
    int hash2 = (second == null ? 0 : second.hashCode());
    return hash1^hash2;
  }




  /**
   * Returns true iff the given Object is a Pair, and its two objects are the
   * same as this one's (comparison done via <code>Utilities.areEqual</code>)
   */

  public boolean equals(Object o){
    if (o == this)
      return true;

    if (!(o instanceof Pair))
      return false;

    Pair pair = (Pair)o;

    return Utilities.areEqual(pair.first, first) && Utilities.areEqual(pair.second, second);
  }

}
 /**
  * The utillib library.
  * More information is available at http://www.jinchess.com/.
  * Copyright (C) 2002, 2003 Alexander Maryanovsky.
  * All rights reserved.
  *
  * The utillib library is free software; you can redistribute
  * it and/or modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
  *
  * The utillib library 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 Lesser
  * General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with utillib library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA
  */



 /**
  * A collection of general utility methods.
  */

  class Utilities{
   
   
   
   /**
    * A 0-length Object array.
    */
    
   public static final Object [] EMPTY_ARRAY = new Object[0];



   /**
    * A 0-length long array.
    */
    
   public static final long [] EMPTY_LONG_ARRAY = new long[0];



   /**
    * A 0-length int array.
    */
    
   public static final int [] EMPTY_INT_ARRAY = new int[0];
   

   
   /**
    * A 0-length short array.
    */
    
   public static final short [] EMPTY_SHORT_ARRAY = new short[0];

   

   /**
    * A 0-length byte array.
    */
    
   public static final byte [] EMPTY_BYTE_ARRAY = new byte[0];
   
   
   
   /**
    * A 0-length char array.
    */
    
   public static final char [] EMPTY_CHAR_ARRAY = new char[0];



   /**
    * A 0-length double array.
    */
    
   public static final double [] EMPTY_DOUBLE_ARRAY = new double[0];
   


   /**
    * A 0-length float array.
    */
    
   public static final float [] EMPTY_FLOAT_ARRAY = new float[0];



   /**
    * A 0-length String array.
    */
    
   public static final String [] EMPTY_STRING_ARRAY = new String[0];
   
   
   
   /**
    * An empty enumeration.
    */
   
   public static final Enumeration EMPTY_ENUM = new Enumeration(){
     public boolean hasMoreElements(){return false;}
     public Object nextElement(){throw new NoSuchElementException();}
   };
   
   

   /**
    * Returns <code>true</code> if the two specified objects are the same.
    * Returns <code>false</code> otherwise. To be considered the same, the two
    * references must either both be null or invoking <code>equals</code> on one
    * of them with the other must return <code>true</code>.
    */

   public static boolean areEqual(Object obj1, Object obj2){
     return (obj1 == obj2) || (obj1 == null ? false : obj1.equals(obj2));
   }




   /**
    * Maps the specified key to the specified value in the specified
    * <code>Hashtable</code>. If the specified value is <code>null</code> any
    * existing mapping of the specified key is removed from the
    * <code>Hashtable</code>. The old value mapped to the specified key
    * is returned, or <code>null</code> if no value was mapped to the key.
    */

   public static Object put(Hashtable table, Object key, Object value){
     return value == null ? table.remove(key) : table.put(key, value);
   }



   /**
    * Returns <code>true</code> if the specified object is an element of the
    * specified array. The specified array may not be <code>null</code>. The
    * specified object may be <code>null</code>, in which case this method will
    * return <code>true</code> iff one of the indices in the array is empty 
    * (contains <code>null</code>).
    */

   public static boolean contains(Object [] array, Object item){
     return (indexOf(array, item) != -1);
   }



   /**
    * Returns the index of the first occurrance of specified object in the
    * specified array, or -1 if the specified object is not an element of the
    * specified array. The specified object may be <code>null</code> in which
    * case the returned index will be the index of the first <code>null</code>
    * in the array.
    */

   public static int indexOf(Object [] array, Object item){
     if (array == null)
       throw new IllegalArgumentException("The specified array may not be null");

     for (int i = 0; i < array.length; i++)
       if (areEqual(item, array[i]))
         return i;

     return -1;
   }




   /**
    * Returns the index of the first occurrance of specified integer in the
    * specified array, or -1 if the specified integer is not an element of the
    * specified array.
    */

   public static int indexOf(int [] arr, int val){
     if (arr == null)
       throw new IllegalArgumentException("The specified array may not be null");

     for (int i = 0; i < arr.length; i++)
       if (arr[i] == val)
         return i;

     return -1;
   }

   
   
   /**
    * Converts the specified array into a string by appending all its elements
    * separated by a semicolon.
    */

   public static String arrayToString(Object [] arr){
     StringBuffer buf = new StringBuffer();
     for (int i = 0; i < arr.length; i++){
       buf.append(arr[i]);
       buf.append("; ");
     }
     if (arr.length > 0)
       buf.setLength(buf.length() - 2); // get rid of the extra "; "

     return buf.toString();
   }




   /**
    * Converts the specified <code>Hashtable</code> into a string by putting
    * each key and value on a separate line (separated by '\n') and an arrow
    * (" -> ") between them.
    */

   public static String hashtableToString(Hashtable hashtable){
     StringBuffer buf = new StringBuffer();
     Enumeration keys = hashtable.keys();
     while (keys.hasMoreElements()){
       Object key = keys.nextElement();
       Object value = hashtable.get(key);
       buf.append(key.toString());
       buf.append(" -> ");
       buf.append(value.toString());
       buf.append("\n");
     }

     return buf.toString();
   }




   /**
    * Returns the maximum element in the specified integer array.
    */

   public static int max(int [] arr){
     if (arr == null)
       throw new IllegalArgumentException("The specified array may not be null");
     if (arr.length == 0)
       throw new IllegalArgumentException("The specified array must have at least one element");

     int n = arr[0];
     for (int i = 1; i < arr.length; i++)
       if (arr[i] > n)
         n = arr[i];

     return n;
   }



   /**
    * Returns a hash code for the specified double value.
    */

   public static int hashCode(double val){
     return hashCode(Double.doubleToLongBits(val));
   }



   /**
    * Returns a hash code for the specified long value.
    */

   public static int hashCode(long val){
     return (int)(val ^ (val >>> 32));
   }
   
   
   
   /**
    * Returns the name of the package of the specified class.
    */
   
   public static String getPackageName(Class c){
     return getPackageName(c.getName());
   }
   
   
   
   /**
    * Returns the name of the package of the class with the specified (full) name.
    */
   
   public static String getPackageName(String className){
     int lastDotIndex = className.lastIndexOf(".");
     return lastDotIndex == -1 ? "" : className.substring(0, lastDotIndex);
   }
   
   
   
   /**
    * Returns the short name (excluding the package name) of the specified class. 
    */
   
   public static String getClassName(Class c){
     return getClassName(c.getName());
   }
   
   
   
   /**
    * Returns the short name (excluding the package name) of the class with the
    * specified fully qualified name.
    */
   
   public static String getClassName(String className){
     int lastDotIndex = className.lastIndexOf(".");
     return lastDotIndex == -1 ? className : className.substring(lastDotIndex + 1);
   }
   
   
   
 }

   
    
    
    
  








Related examples in the same category

1.Custom layout: EdgeLayout
2.Customized layout managerCustomized layout manager
3.ColumnLayoutColumnLayout
4.Applet GUI demo of TreeLayout layout manager
5.Relative Layout Manager for Java J2SE
6.Basically two (or more) columns of different, but constant, widths
7.GraphPaperLayoutGraphPaperLayout
8.Table Layout implements LayoutManager2
9.Table layout manager
10.Flex Layout
11.Square Layout
12.Center Layout
13.Wrapper Layout
14.Tile Layout
15.Custom Layout DemoCustom Layout Demo
16.X Y Layout
17.DividerLayout is layout that divides two components with the column of actions
18.Stack Layout, uses an orientation to determine if the contents should be arranged horizontally or vertically.
19.A simple layoutmanager to overlay all components of a parent.
20.A layout manager that displays a single component in the center of its container.
21.A layout manager that spaces components over six columns in seven different formats.
22.Compents are laid out in a circle.
23.Special simple layout used in TabbedContainer
24.Place components at exact locations (x, y, width, height) and then determine how they behave when the window containing them (their parent) is resized
25.Specialised layout manager for a grid of components.