/* class GridBagLayout
*
* Copyright (C) 2001 R M Pitman
*
* This 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.1 of the License, or (at your option) any later version.
*
* This 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 this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package charva.awt;
import java.util.Enumeration;
import java.util.Vector;
/**
* This is an approximation of the AWT GridBagLayout layout manager.
* See the documentation of the AWT GridBagLayout for details.
*/
public class GridBagLayout
implements LayoutManager2
{
public GridBagLayout()
{
}
/**
* Calculate the geometry for the specified list of Components,
* and return the size of the rectangle that encloses all the
* Components.
*/
public Dimension minimumSize(Container container_)
{
/* First work out the dimensions of the grid (i.e. the number of
* rows and columns). We do this by iterating through all the
* added components, and inspecting their gridx, gridy, gridwidth and
* gridheight constraints.
*/
_rows = 0;
_columns = 0;
Enumeration<GridBagConstraints> e1 = _constraints.elements();
while (e1.hasMoreElements()) {
GridBagConstraints gbc = e1.nextElement();
if (gbc.gridx + gbc.gridwidth > _columns)
_columns = gbc.gridx + gbc.gridwidth;
if (gbc.gridy + gbc.gridheight > _rows)
_rows = gbc.gridy + gbc.gridheight;
}
/* Now that we know the number of rows and columns, we can create
* arrays to hold the row heights and column widths.
*/
_calculatedRowHeights = new int[_rows];
_calculatedRowWeights = new double[_rows];
_calculatedColumnWidths = new int[_columns];
_calculatedColumnWeights = new double[_columns];
/* Create a pair of arrays, each the same length as the number of
* contained components, to hold the "width-left" and "height-left"
* values for each component.
*/
int width_left[] = new int[_components.size()];
int height_left[] = new int[_components.size()];
Enumeration<Component> e3 = _components.elements();
Enumeration<GridBagConstraints> e2 = _constraints.elements();
for (int i=0; e3.hasMoreElements(); i++) {
Component c = e3.nextElement();
GridBagConstraints gbc = e2.nextElement();
/* Calculate the minimum width & height required for this
* component.
*/
Insets insets = gbc.insets;
Dimension minsize = c.minimumSize();
width_left[i] =
minsize.width + insets.left + insets.right;
height_left[i] =
minsize.height + insets.top + insets.bottom;
}
/* Now iterate through all the rows and allocate heights to each row.
*/
for (int row=0; row<_rows; row++) {
/* Iterate through the constraints and find those whose bottom edge
* is in the current row. The one with the maximum height_left
* value determines the height of this row.
*/
_calculatedRowHeights[row] = 0;
Enumeration<GridBagConstraints> e = _constraints.elements();
for (int i=0; e.hasMoreElements(); i++) {
GridBagConstraints gbc = e.nextElement();
if (row == gbc.gridy + gbc.gridheight - 1) {
/* This component's bottom edge is in the current row.
*/
if (height_left[i] > _calculatedRowHeights[row])
_calculatedRowHeights[row] = height_left[i];
}
}
/* Now that we have calculated the height of this row, subtract
* this row-height from the height_left value of each component
* which extends to or below this row.
*/
e = _constraints.elements();
for (int i=0; e.hasMoreElements(); i++) {
GridBagConstraints gbc = e.nextElement();
if (row >= gbc.gridy &&
row < gbc.gridy + gbc.gridheight) {
height_left[i] -= _calculatedRowHeights[row];
}
}
}
/* Now iterate through all the columns and allocate widths to each
* column.
*/
for (int column=0; column<_columns; column++) {
/* Iterate through the constraints and find those whose right edge
* is in the current column. The one with the maximum width_left
* value determines the width of this column.
*/
_calculatedColumnWidths[column] = 0;
Enumeration<GridBagConstraints> e = _constraints.elements();
for (int i=0; e.hasMoreElements(); i++) {
GridBagConstraints gbc = e.nextElement();
if (column == gbc.gridx + gbc.gridwidth - 1) {
/* This component's right edge is in the current column.
*/
if (width_left[i] > _calculatedColumnWidths[column])
_calculatedColumnWidths[column] = width_left[i];
}
}
/* Now that we have calculated the width of this column, subtract
* this column-width from the width_left value of each component
* which extends to or to the right of this column.
*/
e = _constraints.elements();
for (int i=0; e.hasMoreElements(); i++) {
GridBagConstraints gbc = e.nextElement();
if (column >= gbc.gridx &&
column < gbc.gridx + gbc.gridwidth) {
width_left[i] -= _calculatedColumnWidths[column];
}
}
}
/* Iterate through all the components and calculate the row
* and column weights.
*/
e2 = _constraints.elements();
while (e2.hasMoreElements()) {
GridBagConstraints gbc = e2.nextElement();
for (int i=gbc.gridx; i< gbc.gridx + gbc.gridwidth; i++) {
if (gbc.weightx > _calculatedColumnWeights[i])
_calculatedColumnWeights[i] = gbc.weightx;
}
for (int i=gbc.gridy; i< gbc.gridy + gbc.gridheight; i++) {
if (gbc.weighty > _calculatedRowWeights[i])
_calculatedRowWeights[i] = gbc.weighty;
}
}
/* Now just add up all the column widths and row heights to find
* the minimum size of the container.
*/
Insets insets = container_.getInsets();
int totalwidth = insets.left + insets.right;
int totalheight = insets.top + insets.bottom;
for (int i=0; i<_columns; i++) {
totalwidth += _calculatedColumnWidths[i];
_totalweightx += _calculatedColumnWeights[i];
}
for (int i=0; i<_rows; i++) {
totalheight += _calculatedRowHeights[i];
_totalweighty += _calculatedRowWeights[i];
}
return new Dimension(totalwidth, totalheight);
}
/**
* Set the positions of the contained components.
*/
public void doLayout(Container container_)
{
Insets insets = container_.getInsets();
Dimension size = container_.getSize();
Dimension minsize = minimumSize(container_);
int extraColumns = size.width - minsize.width;
int extraRows = size.height - minsize.height;
Enumeration<Component> e1 = _components.elements();
Enumeration<GridBagConstraints> e2 = _constraints.elements();
while (e1.hasMoreElements()) {
Component c = e1.nextElement();
GridBagConstraints gbc = e2.nextElement();
/* Calculate the boundaries of the grid cell that this
* component occupies.
*/
int left = insets.left;
if (_totalweightx == 0.0)
left += extraColumns/2;
for (int i=0; i<gbc.gridx; i++) {
left += _calculatedColumnWidths[i];
if (_totalweightx != 0.0)
left += (extraColumns * _calculatedColumnWeights[i]) /
_totalweightx;
}
int right = left;
for (int i=0; i<gbc.gridwidth; i++)
right += _calculatedColumnWidths[gbc.gridx + i];
int top = insets.top;
if (_totalweighty == 0.0)
top += extraRows/2;
for (int i=0; i<gbc.gridy; i++) {
top += _calculatedRowHeights[i];
if (_totalweighty != 0.0)
top += (extraRows * _calculatedRowWeights[i]) /
_totalweighty;
}
int bottom = top;
for (int i=0; i<gbc.gridheight; i++)
bottom += _calculatedRowHeights[gbc.gridy + i];
if (c instanceof Container) {
Container cont = (Container) c;
/* Get the contained container to lay itself out at its
* preferred size, if it is not already laid out.
*/
if (cont.isValid() == false)
cont.setSize(cont.minimumSize());
switch (gbc.fill) {
case GridBagConstraints.NONE:
break;
case GridBagConstraints.HORIZONTAL:
cont.setWidth(right - left);
break;
case GridBagConstraints.VERTICAL:
cont.setHeight(bottom - top);
break;
case GridBagConstraints.BOTH:
cont.setSize(right - left, bottom - top);
break;
default:
throw new IllegalArgumentException(
"Invalid fill parameter");
}
cont.doLayout();
}
/* Calculate the x position of the component's origin (i.e. top
* left corner).
*/
int cx = 0;
switch (gbc.anchor) {
case GridBagConstraints.WEST:
case GridBagConstraints.NORTHWEST:
case GridBagConstraints.SOUTHWEST:
cx = left + gbc.insets.left;
break;
case GridBagConstraints.EAST:
case GridBagConstraints.NORTHEAST:
case GridBagConstraints.SOUTHEAST:
cx = right - gbc.insets.right - c.getSize().width;
break;
case GridBagConstraints.CENTER:
case GridBagConstraints.NORTH:
case GridBagConstraints.SOUTH:
cx = (left + gbc.insets.left) +
(right - gbc.insets.right);
cx -= c.getSize().width;
cx = cx / 2;
break;
default:
throw new IllegalArgumentException(
"invalid anchor paremeter");
}
/* Calculate the y position of the component's origin (i.e. top
* left corner).
*/
int cy = 0;
switch (gbc.anchor) {
case GridBagConstraints.NORTH:
case GridBagConstraints.NORTHWEST:
case GridBagConstraints.NORTHEAST:
cy = top + gbc.insets.top;
break;
case GridBagConstraints.SOUTH:
case GridBagConstraints.SOUTHWEST:
case GridBagConstraints.SOUTHEAST:
cy = bottom - gbc.insets.bottom - c.getSize().height;
break;
case GridBagConstraints.CENTER:
case GridBagConstraints.WEST:
case GridBagConstraints.EAST:
cy = (top + gbc.insets.top) +
(bottom - gbc.insets.bottom);
cy -= c.getSize().height;
cy = cy / 2;
}
c.setLocation(cx, cy);
}
}
public void addLayoutComponent(Component component_, Object constraint_)
{
_components.add(component_);
/* Make a copy of the constraints object passed to us, so that the
* caller can re-use it for other components.
*/
GridBagConstraints constraint = (GridBagConstraints) constraint_;
GridBagConstraints newc = new GridBagConstraints();
newc.gridx = constraint.gridx;
newc.gridy = constraint.gridy;
newc.gridwidth = constraint.gridwidth;
newc.gridheight = constraint.gridheight;
newc.weightx = constraint.weightx;
newc.weighty = constraint.weighty;
newc.anchor = constraint.anchor;
newc.fill = constraint.fill;
newc.insets = new Insets(
constraint.insets.top,
constraint.insets.left,
constraint.insets.bottom,
constraint.insets.right);
newc.ipadx = constraint.ipadx;
newc.ipady = constraint.ipady;
_constraints.add(newc);
}
/**
* Invalidates the layout, indicating that if the layout manager has cached
* information it should be discarded.
*/
public void invalidateLayout(Container target_) {
}
//====================================================================
// INSTANCE VARIABLES
/**
* This field holds the overrides to the column minimum widths.
*/
public int[] columnWidths;
/**
* This field holds the overrides to the row minimum heights.
*/
public int[] rowHeights;
/**
* This field is not used in the CHARVA package but is present to allow
* compile-time compatibility with AWT.
*/
public double[] columnWeights;
/**
* This field is not used in the CHARVA package but is present to allow
* compile-time compatibility with AWT.
*/
public double[] rowWeights;
/**
* As components are added, they are stored in this vector.
*/
private Vector<Component> _components = new Vector<Component>();
/**
* As components are added, their constraint objects are stored in
* this vector.
*/
private Vector<GridBagConstraints> _constraints = new Vector<GridBagConstraints>();
/** The number of rows in the grid (calculated from all the added
* components and their gridx, gridy, gridwidth and gridheight
* constraints).
*/
private int _rows;
/** The number of rows in the grid (calculated from all the added
* components and their gridx, gridy, gridwidth and gridheight
* constraints).
*/
private int _columns;
/**
* This array holds the row heights that we calculate.
*/
private int[] _calculatedRowHeights;
/**
* This array holds the columns widths that we calculate.
*/
private int[] _calculatedColumnWidths;
/**
* This array holds the row weights that we calculate.
*/
private double[] _calculatedRowWeights;
/**
* This array holds the column weights that we calculate.
*/
private double[] _calculatedColumnWeights;
private double _totalweightx = 0.0;
private double _totalweighty = 0.0;
}