package org.tn5250j.tools;
/**
 * Title: tn5250J
 * Copyright:   Copyright (c) 2001
 * Company:
 * @author  Kenneth J. Pouncey
 * @version 0.1
 *
 * Description:
 *
 * 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, 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 software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA
 *
 */
import java.awt.GridLayout;
import java.awt.Container;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;

/**
 * <code>ENHGridLayout</code> is an improved subclass of <code>GridLayout</code>.
 * It lays out a grid of rows and columns based on the attributes of the
 * individual rows and columns. <code>ENHGridLayout</code>
 * uses the widest element in a column to set the width of that
 * column, and the tallest element in a row to set the height of
 * that row.
 */
public class ENHGridLayout extends GridLayout {

   private static final long serialVersionUID = 1L;

/** The horiztonal gap between items. */
   protected int hgap;

   /** The vertical gap between items. */
   protected int vgap;

   /** The number of rows in the layout, as set by the user.
    * This number may not correspond exactly to the number of
    * rows in the layout.
    */
   protected int rows;

   /** The number of columns in the layout, as set by the user.
    * This number may not correspond exactly to the number of
    * columns in the layout.
    */
   protected int cols;

   /** Array of row heights.
    * It is accurate only after a call to getGridSizes()
    */
   protected int row_heights[] = new int[0];

   /** Array of column widths.
    * It is accurate only after a call to getGridSizes()
    */
   protected int col_widths[] = new int[0];

   public final static int VARIABLE = 0;

   /**
    * Creates a grid layout with the specified number of rows and columns.
    * @param rows the number of rows in the layout
    * @param cols the number of columns in the layout
    */
   public ENHGridLayout(int rows, int cols) {
      this(rows, cols, 0, 0);
   }

   /**
    * Creates a grid layout with the specified rows, columns,
    * horizontal gap, and vertical gap.
    * @param rows the rows; VARIABLE (0) means 'any number.'
    * @param cols the columns; VARIABLE (0) means 'any number.'
    * Only one of 'rows' and 'cols' can be VARIABLE, not both.
    * @param hgap the horizontal gap variable
    * @param vgap the vertical gap variable
    * @exception IllegalArgumentException If the rows and columns are invalid.
    */
   public ENHGridLayout(int rows, int cols, int hgap, int vgap) {
      super(rows, cols, hgap, vgap);
      this.rows = rows;
      this.cols = cols;
      this.hgap = hgap;
      this.vgap = vgap;
   }

   /**
    * Traverses the children and determines row heights and column widths.
    * @param parent the component which needs to be laid out
    * @param min if true, the minimum size is used. Otherwise, the preferred size
    * is used.
    */
   protected void getGridSizes(Container parent, boolean min) {
      int ncomponents = parent.getComponentCount();
      if (ncomponents == 0) return;
      int nrows = rows, ncols = cols;
      if (nrows > 0)
          ncols = (ncomponents + nrows - 1) / nrows;
      else
          nrows = (ncomponents + ncols - 1) / ncols;

      row_heights = new int[nrows];
      col_widths = new int[ncols];

      for (int i = 0; i < ncomponents; i++) {
          Component comp = parent.getComponent(i);
         Dimension d = min ? comp.getMinimumSize() :
                      comp.getPreferredSize();

         int row = i / ncols;
         if (d.height > row_heights[row])
            row_heights[row] = d.height;

         int col = i % ncols;
         if (d.width > col_widths[col])
            col_widths[col] = d.width;
      }
   }

   /**
    * Sums the items of an array
    */
   final int sum(int[] array) {
      if (array == null) return 0;
      int s = 0;
      for (int i = 0; i < array.length; i++)
         s += array[i];
      return s;
   }

   /**
    * Calculates the preferred size for this layout.
    * @param parent the component which needs to be laid out
    */
   public Dimension preferredLayoutSize(Container parent) {

      Insets insets = parent.getInsets();
      getGridSizes(parent, false);
      return new Dimension(insets.left + insets.right + sum(col_widths)
                      + (col_widths.length+1)*hgap,
                      insets.top + insets.bottom + sum(row_heights)
                      + (row_heights.length+1)*vgap);
   }

   /**
    * Returns the minimum dimensions needed to layout the components
    * contained in the specified panel.
    * @param parent the component which needs to be laid out
    */
   public Dimension minimumLayoutSize(Container parent) {

      Insets insets = parent.getInsets();
      getGridSizes(parent, true);
      return new Dimension(insets.left + insets.right + sum(col_widths)
                      + (col_widths.length+1)*hgap,
                      insets.top + insets.bottom + sum(row_heights)
                      + (row_heights.length+1)*vgap);
   }

   /**
    * Positions the component.
    * @param pos the component's index in its parents child list
    * @param row,col component's position
    */
   protected void setBounds(int pos, int row, int col,
                     Component comp, int x, int y, int w, int h) {

      comp.setBounds(x, y, w, h);

   }

   /**
    * Performs the layout of the children.
    * It calculates the number of actual rows and columns
    * based on the user's settings, retrieves row height and column
    * width information, then moves all the children to the appropriate places.
    * @param parent the specified component being laid out
    * @see #reshape
    */
   public void layoutContainer(Container parent) {
      int ncomponents = parent.getComponentCount();
      if (ncomponents == 0) {
         return;
      }
      Insets insets = parent.getInsets();

      getGridSizes(parent, false);
      int nrows = rows, ncols = cols;

      if (nrows > 0)
          ncols = (ncomponents + nrows - 1) / nrows;
      else
          nrows = (ncomponents + ncols - 1) / ncols;

      Dimension psize = parent.getSize();
      for (int col = 0, x = insets.left+hgap; col < ncols; col++) {
          for (int row = 0, y = insets.top+vgap; row < nrows; row++) {
            int i = row*ncols + col;
            if (i < ncomponents) {
               int w = Math.max(0, Math.min(col_widths[col],
                                     psize.width-insets.right-x));
               int h = Math.max(0, Math.min(row_heights[row],
                                     psize.height-insets.bottom-y));
               setBounds(i, row, col, parent.getComponent(i), x, y, w, h);
            }
            y += row_heights[row] + vgap;
          }
         x += col_widths[col] + hgap;
      }
   }

}