acm.gui.TableLayout Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javakarel Show documentation
Show all versions of javakarel Show documentation
This the original Stanford Karel for Java, packaged for Maven. ACM Library is included. See also https://cs.stanford.edu/people/eroberts/karel-the-robot-learns-java.pdf
The newest version!
/*
* @(#)TableLayout.java 1.99.1 08/12/08
*/
// ************************************************************************
// * Copyright (c) 2008 by the Association for Computing Machinery *
// * *
// * The Java Task Force seeks to impose few restrictions on the use of *
// * these packages so that users have as much freedom as possible to *
// * use this software in constructive ways and can make the benefits of *
// * that work available to others. In view of the legal complexities *
// * of software development, however, it is essential for the ACM to *
// * maintain its copyright to guard against attempts by others to *
// * claim ownership rights. The full text of the JTF Software License *
// * is available at the following URL: *
// * *
// * http://www.acm.org/jtf/jtf-software-license.pdf *
// * *
// ************************************************************************
// REVISION HISTORY
//
// -- V2.0 --
// Bug fix 8-Jan-06 (ESR, JTFBug 2008-001)
// 1. Fixed bug in the layoutContainer failed to consider the gridwidth
// and gridheight values in computing the width of a column.
//
// Code cleanup 28-May-07 (ESR)
// 1. Added generic type tags.
//
// Feature enhancement 26-May-08 (ESR)
// 1. Added support for serialization.
package acm.gui;
import acm.util.*;
import java.awt.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
/* Class: TableLayout */
/**
* This class implements a new LayoutManager
that arranges
* components into a two-dimensional grid. The capabilities are analogous
* to those provide by the GridBagLayout
class, although
* TableLayout
is considerably easier to use.
*
* The constructor for the TableLayout
class takes the number
* of rows and columns. Thus, the constructor call
*
*
* new TableLayout(3, 5)
*
*
*
creates a new TableLayout
manager with three rows in the
* vertical dimension and five columns across the page. Components are added
* to the grid starting at the upper left, filling each horizontal row and
* then moving on to the next row down after the current one is filled.
*
*
The TableLayout
manager also supports more fine-grained
* control over the formatting of tables by allowing you to specify
* constraints in the following form:
*
*
* constraint = value
*
*
*
For example, if you want to specify that a component occupies two
* columns in the grid, you would use the constraint
*
*
* "gridwidth=2"
*
*
*
Strings used as constraint objects can set several constraints at
* once by including multiple constraint/value pairs separated by spaces.
*
*
The TableLayout
class accepts all constraints supported
* by GridBagLayout
.
*/
public class TableLayout implements LayoutManager2, Serializable {
/** Do not resize component */
public static final int NONE = GridBagConstraints.NONE;
/** Resize component in horizontal direction only */
public static final int HORIZONTAL = GridBagConstraints.HORIZONTAL;
/** Resize component in vertical direction only */
public static final int VERTICAL = GridBagConstraints.VERTICAL;
/** Resize component in both directions */
public static final int BOTH = GridBagConstraints.BOTH;
/** Center table in the container */
public static final int CENTER = 10;
/** Align table horizontally at the left of its container */
public static final int LEFT = 11;
/** Align table horizontally at the right of its container */
public static final int RIGHT = 12;
/** Align table vertically at the top of its container */
public static final int TOP = 13;
/** Align table vertically at the bottom of its container */
public static final int BOTTOM = 14;
/** Expand table to fill its container */
public static final int FILL = BOTH;
/* Constructor: TableLayout() */
/**
* Creates a new TableLayout
object with no limits
* on the number of rows and columns.
*
* Example: TableLayout layout = new TableLayout();
*/
public TableLayout() {
this(0, 0);
}
/* Constructor: TableLayout(rows, columns) */
/**
* Creates a new TableLayout
object with the specified
* number of rows and columns.
*
* Example: TableLayout layout = new TableLayout(rows, columns);
* @param rows The number of rows, or 0 for no limit
* @param columns The number of columns, or 0 for no limit
*/
public TableLayout(int rows, int columns) {
this(rows, columns, 0, 0);
}
/* Constructor: TableLayout(rows, columns, hgap, vgap) */
/**
* Creates a new TableLayout
object with the specified
* row count, column count, alignment, horizontal gap, and vertical gap.
*
* Example: TableLayout layout = new TableLayout(rows, columns, hgap, vgap);
* @param rows The number of rows, or 0 for no limit
* @param columns The number of columns, or 0 for no limit
* @param hgap The horizontal gap between columns
* @param vgap The vertical gap between rows
*/
public TableLayout(int rows, int columns, int hgap, int vgap) {
nRows = rows;
nColumns = columns;
hGap = hgap;
vGap = vgap;
horizontalAlignment = CENTER;
verticalAlignment = CENTER;
defaultFill = BOTH;
constraintTable = new HashMap();
propertyTable = new HashMap();
layoutTable = null;
}
/* Method: setColumnCount(columns) */
/**
* Resets the number of columns in the table.
*
* Example: layout.setColumnCount(columns);
* @param columns The new number of columns
*/
public void setColumnCount(int columns) {
nColumns = columns;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getColumnCount() */
/**
* Returns the number of columns in the table.
*
* Example: int columns = layout.getColumnCount();
* @return The number of columns
*/
public int getColumnCount() {
return nColumns;
}
/* Method: setRowCount(rows) */
/**
* Resets the number of rows in the table.
*
* Example: layout.setRowCount(rows);
* @param rows The new number of rows
*/
public void setRowCount(int rows) {
nRows = rows;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getRowCount() */
/**
* Returns the number of rows in the table.
*
* Example: int rows = layout.getRowCount();
* @return The number of rows
*/
public int getRowCount() {
return nRows;
}
/* Method: setHorizontalAlignment(align) */
/**
* Sets the horizontal alignment for the table. The legal values
* are CENTER
, LEFT
, RIGHT
, and
* FILL
.
*
* Example: layout.setHorizontalAlignment(align);
* @param align The horizontal alignment for the table
*/
public void setHorizontalAlignment(int align) {
horizontalAlignment = align;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getHorizontalAlignment() */
/**
* Returns the horizontal alignment for the table.
*
* Example: int align = layout.getHorizontalAlignment();
* @return The horizontal alignment for the table
*/
public int getHorizontalAlignment() {
return horizontalAlignment;
}
/* Method: setVerticalAlignment(align) */
/**
* Sets the vertical alignment for the table. The legal values
* are CENTER
, TOP
, BOTTOM
, and
* FILL
.
*
* Example: layout.setVerticalAlignment(align);
* @param align The vertical alignment for the table
*/
public void setVerticalAlignment(int align) {
verticalAlignment = align;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getVerticalAlignment() */
/**
* Returns the vertical alignment for the table.
*
* Example: int align = layout.getVerticalAlignment();
* @return The vertical alignment for the table
*/
public int getVerticalAlignment() {
return verticalAlignment;
}
/* Method: setDefaultFill(fill) */
/**
* Sets the default fill parameter for components in the table. The legal values
* are NONE
, HORIZONTAL
, VERTICAL
, and
* BOTH
.
*
* Example: layout.setDefaultFill(fill);
* @param fill The default fill parameter for components in the table
*/
public void setDefaultFill(int fill) {
defaultFill = fill;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getDefaultFill() */
/**
* Returns the default fill parameter for components in the table.
*
* Example: int fill = layout.getDefaultFill();
* @return The default fill parameter for components in the table
*/
public int getDefaultFill() {
return defaultFill;
}
/* Method: setHgap(pixels) */
/**
* Sets the horizontal gap between components.
*
* Example: layout.setHgap(pixels);
* @param pixels The gap between components in pixels
*/
public void setHgap(int pixels) {
hGap = pixels;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getHgap() */
/**
* Returns the horizontal gap between components.
*
* Example: int hgap = layout.getHgap();
* @return The horizontal gap between components
*/
public int getHgap() {
return hGap;
}
/* Method: setVgap(pixels) */
/**
* Sets the vertical gap between components.
*
* Example: layout.setVgap(pixels);
* @param pixels The gap between components in pixels
*/
public void setVgap(int pixels) {
vGap = pixels;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: getVgap() */
/**
* Returns the vertical gap between components.
*
* Example: int vgap = layout.getVgap();
* @return The vertical gap between components
*/
public int getVgap() {
return vGap;
}
/* Method: setStrictGridBagModel(flag) */
/**
* Sets a flag indicating whether the layout manager should match the
* model used in GridBagLayout
even when that interpretation
* seems wrong. The differences show up in the following areas:
*
*
* - Calculation of cell size.
GridBagLayout
uses
* the minimum layout size of the component to determine the size of each
* cell; TableLayout
uses the preferred size. In practice,
* these two values are often the same, but it seems that the preferred
* size makes considerably more sense in terms of how this layout is used.
*
* - Treatment of cells spanning several rows. In
*
GridBagLayout
, each new row begins after the last
* multirow cell in that row, even if there is space to its left.
* By default, TableLayout
uses the HTML model in which
* the cells begin at the left margin unless that column is itself
* already occupied.
*
*
* Example: layout.setStrictGridBagModel(flag);
* @param flag true
to use a strict GridBagLayout
model
*
*/
public void setStrictGridBagModel(boolean flag) {
useStrictGridBagModel = flag;
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: isStrictGridBagModel() */
/**
* Returns true
if this layout manager is treating multirow
* cells exactly as GridBagLayout
does.
*
* Example: if (layout.isStrictGridBagModel()) . . .
* @return true
if this manager is using the strict GridBagLayout
model
*
*/
public boolean isStrictGridBagModel() {
return useStrictGridBagModel;
}
/* Method: setConstraints(comp, constraints) */
/**
* Sets the constraints for the component to a copy of the supplied
* constraints.
*
* Example: layout.setConstraints(comp, constraints);
* @param comp The component to be constrained
* @param constraints The constraints object used to specify the layout
*/
public void setConstraints(Component comp, GridBagConstraints constraints) {
constraintTable.put(comp, (GridBagConstraints) constraints.clone());
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: setConstraints(comp, constraints) */
/**
* Sets the constraints for the component to the constraints
* specified by the string.
*
* Example: layout.setConstraints(comp, constraints);
* @param comp The component to be constrained
* @param constraints A string specifying the constraints
*/
public void setConstraints(Component comp, String constraints) {
setConstraints(comp, new TableConstraints(constraints));
}
/* Method: getConstraints(comp) */
/**
* Returns a copy of the constraints requested for the specified component. The
* constraints value is always converted to a TableConstraints
so that
* clients can use this class in preference to GridBagConstraints
* without needing a type cast.
*
* Example: TableConstraints tc = layout.getConstraints(comp);
* @param comp The component whose constraints are requested
* @return A copy of the constraints object used to specify the layout
*/
public TableConstraints getConstraints(Component comp) {
GridBagConstraints gbc = lookupConstraints(comp);
return (gbc == null) ? null : new TableConstraints(gbc);
}
/* LayoutManager interface */
/* Method: addLayoutComponent(constraints, comp) */
/**
* Adds a component to the layout. Implements LayoutManager
.
*
* @param constraints The constraint string
* @param comp The component to be added
*
*/
public void addLayoutComponent(String constraints, Component comp) {
addLayoutComponent(comp, constraints);
}
/* Method: removeLayoutComponent(comp) */
/**
* Removes the specified component from the layout.
*
* @param comp The component to be removed
*
*/
public void removeLayoutComponent(Component comp) {
constraintTable.remove(comp);
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: preferredLayoutSize(target) */
/**
* Calculates the preferred size for the FrameLayout
component
* when laid out in the target
container.
*
* @param target The container in which the layout is done
* @return The preferred dimensions for the layout
*
*/
public Dimension preferredLayoutSize(Container target) {
if (target.getComponentCount() == 0) return new Dimension(0, 0);
return processLayout(target, PREFERRED_LAYOUT_SIZE_MODE);
}
/* Method: minimumLayoutSize(target) */
/**
* Calculates the minimum size for the FrameLayout
component
* when laid out in the target
container.
*
* @param target The container in which the layout is done
* @return The minimum dimensions for the layout
*
*/
public Dimension minimumLayoutSize(Container target) {
if (target.getComponentCount() == 0) return new Dimension(0, 0);
return processLayout(target, MINIMUM_LAYOUT_SIZE_MODE);
}
/* Method: layoutContainer(target) */
/**
* Lays out the components in the target container.
*
* @param target The container in which the layout is done
*
*/
public void layoutContainer(Container target) {
targetContainer = target;
processLayout(target, LAYOUT_CONTAINER_MODE);
}
/* Method: addLayoutComponent(comp, constraints) */
/**
* Adds a component to the layout. The TableLayout
* class overrides this method to allow for string constraints.
*
* @param comp The component to be added
* @param constraints The constraint object
*
*/
public void addLayoutComponent(Component comp, Object constraints) {
if (constraints == null) {
constraints = new TableConstraints("");
((TableConstraints) constraints).fill = defaultFill;
} else if (constraints instanceof String) {
OptionTable options = new OptionTable(((String) constraints).toLowerCase(), TableConstraints.LEGAL_KEYS);
constraints = new TableConstraints(options.getMap());
if (!options.isSpecified("fill")) {
((TableConstraints) constraints).fill = (options.isSpecified("anchor")) ? NONE : defaultFill;
}
} else if (!(constraints instanceof GridBagConstraints)) {
throw new ErrorException("TableLayout: Illegal constraints");
}
constraintTable.put(comp, (GridBagConstraints) constraints);
if (targetContainer != null) targetContainer.invalidate();
}
/* Method: maximumLayoutSize(target) */
/**
* Calculates the maximum size for the FrameLayout
component
* when laid out in the target
container.
*
* @param target The container in which the layout is done
* @return The maximum dimensions for the layout
*
*/
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
/* Method: getLayoutAlignmentX(target) */
/**
* Returns the alignment along the x-axis as described in the
* LayoutManager2
interface.
*
* @param target The container in which the layout is done
* @return A value between 0 and 1 indicating where this component should align
*
*/
public float getLayoutAlignmentX(Container target) {
return 0.5F;
}
/* Method: getLayoutAlignmentY(target) */
/**
* Returns the alignment along the y-axis as described in the
* LayoutManager2
interface.
*
* @param target The container in which the layout is done
* @return A value between 0 and 1 indicating where this component should align
*
*/
public float getLayoutAlignmentY(Container target) {
return 0.5F;
}
/* Method: invalidateLayout(target) */
/**
* Indicates that the layout is no longer valid.
*
* @param target The container in which the layout is done
*
*/
public synchronized void invalidateLayout(Container target) {
layoutTable = null;
Iterator iterator = propertyTable.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
if (key.startsWith("width") && !key.equals("width")) {
propertyTable.put(key, new Integer(0));
}
if (key.startsWith("height") && !key.equals("height")) {
propertyTable.put(key, new Integer(0));
}
if (key.startsWith("weightx") && !key.equals("weightx")) {
propertyTable.put(key, new Double(0));
}
if (key.startsWith("weighty") && !key.equals("weighty")) {
propertyTable.put(key, new Double(0));
}
}
}
/* Override method: toString() */
/**
* Creates a printable representation of the layout.
*
*/
public String toString() {
String str = getClass().getName();
str += "[rows=" + nRows + ",columns=" + nColumns;
if (hGap != 0) str += ",hgap=" + hGap;
if (vGap != 0) str += ",vgap=" + vGap;
str += "]";
return str;
}
/* Protected method: lookupConstraints(comp) */
/**
* Returns the constraints object for the specified component.
*
* Example: GridBagConstraints gbc = layout.lookupConstraints(comp);
* @param comp The component to be constrained
* @return The constraints object used to specify the layout
*/
protected GridBagConstraints lookupConstraints(Component comp) {
return constraintTable.get(comp);
}
/* Protected method: lookupConstraints(comp, target) */
/**
* Returns the TableConstraints
object actually applied to the specified
* component when it is laid out in the target container. In the result of this method
* the values of the gridx
, gridx
, gridwidth
,
* and gridheight
fields are filled in according to the actual position
* in the grid.
*
* Example: TableConstraints tc = layout.lookupConstraints(comp, target);
* @param comp The component to be constrained
* @param target The container in which the layout is done
* @return The constraints object actually applied to the layout
*/
protected TableConstraints lookupConstraints(Component comp, Container target) {
synchronized (target.getTreeLock()) {
if (layoutTable == null) computeLayoutTable(target);
return layoutTable.get(comp);
}
}
/* Protected method: getMinimumComponentSize(comp) */
/**
* This method returns the minimum size of the component for this layout. Subclasses
* can override this methods to establish specific bounds for certain component
* classes, such as the code for scrollbars given here.
*
* Example: Dimension size = layout.getMinimumComponentSize(comp);
* @param comp The component whose size is being tested
* @return The minimum size for that component
*/
private Dimension getMinimumComponentSize(Component comp) {
if (isScrollbar(comp)) return getMinimumScrollbarSize(comp);
return comp.getMinimumSize();
}
/* Protected method: getPreferredComponentSize(comp) */
/**
* This method returns the preferred size of the component for this layout. Subclasses
* can override this methods to establish specific bounds for certain component
* classes, such as the code for scrollbars given here.
*
* Example: Dimension size = layout.getPreferredComponentSize(comp);
* @param comp The component whose size is being tested
* @return The preferred size for that component
*/
private Dimension getPreferredComponentSize(Component comp) {
if (isScrollbar(comp)) return getMinimumScrollbarSize(comp);
return comp.getPreferredSize();
}
/* Protected method: isScrollbar(comp) */
/**
* Determines whether the component is a scroll bar or slider.
*/
private boolean isScrollbar(Component comp) {
return (comp instanceof Scrollbar || comp instanceof JScrollBar || comp instanceof JSlider);
}
/* Protected method: getMinimumScrollbarSize(comp) */
/**
* Determines the minimum dimensions for this scrollbar, making sure it has enough
* space to be displayed with a nonzero slider area.
*/
private Dimension getMinimumScrollbarSize(Component comp) {
Dimension size = comp.getMinimumSize();
try {
Class> scrollbarClass = comp.getClass();
Method getOrientation = scrollbarClass.getMethod("getOrientation", new Class[0]);
int orientation = ((Integer) getOrientation.invoke(comp, new Object[0])).intValue();
if (orientation == Scrollbar.HORIZONTAL) {
size.width = Math.max(size.width, MINIMUM_SCROLLBAR_SIZE);
} else {
size.height = Math.max(size.height, MINIMUM_SCROLLBAR_SIZE);
}
} catch (Exception ex) {
throw new ErrorException(ex);
}
return size;
}
/* Private method: processLayout(target, caller) */
/**
* Common code for the different methods requiring layout synchronization.
* The caller
argument is one of the three constants
* LAYOUT_CONTAINER_MODE, PREFERRED_LAYOUT_SIZE_MODE, or MINIMUM_LAYOUT_SIZE_MODE,
* depending on the caller.
*/
private Dimension processLayout(Container target, int caller) {
synchronized (target.getTreeLock()) {
return lockedProcessLayout(target, caller);
}
}
/* Private method: lockedProcessLayout(target, caller) */
/**
* Method to do the work of processLayout
after synchronization.
*/
private synchronized Dimension lockedProcessLayout(Container target, int caller) {
Dimension result = null;
if (layoutTable == null) computeLayoutTable(target);
int nr = getIntProperty("nRows");
int nc = getIntProperty("nColumns");
int[] heights = new int[nr];
int[] widths = new int[nc];
int nComponents = target.getComponentCount();
for (int i = 0; i < nComponents; i++) {
Component comp = target.getComponent(i);
TableConstraints tc = layoutTable.get(comp);
int column = tc.gridx;
int row = tc.gridy;
int width = getIntProperty("width" + column);
int height = getIntProperty("height" + row);
if (width == 0) {
if (caller == MINIMUM_LAYOUT_SIZE_MODE || useStrictGridBagModel) {
width = getMinimumComponentSize(comp).width;
} else {
width = getPreferredComponentSize(comp).width;
}
width += 2 * tc.ipadx + tc.insets.left + tc.insets.right;
}
if (height == 0) {
if (caller == MINIMUM_LAYOUT_SIZE_MODE || useStrictGridBagModel) {
height = getMinimumComponentSize(comp).height;
} else {
height = getPreferredComponentSize(comp).height;
}
height += 2 * tc.ipady + tc.insets.top + tc.insets.bottom;
}
if (tc.gridwidth <= 1) {
widths[column] = Math.max(widths[column], width);
}
if (tc.gridheight <= 1) {
heights[row] = Math.max(heights[row], height);
}
}
int width = hGap;
int height = vGap;
double[] weightX = new double[nc];
double[] weightY = new double[nr];
double totalX = 0;
double totalY = 0;
for (int column = 0; column < nc; column++) {
width += widths[column] + hGap;
weightX[column] = getDoubleProperty("weightx" + column);
totalX += weightX[column];
}
for (int row = 0; row < nr; row++) {
height += heights[row] + vGap;
weightY[row] = getDoubleProperty("weighty" + row);
totalY += weightY[row];
}
if (caller == LAYOUT_CONTAINER_MODE) {
Dimension size = target.getSize();
Insets insets = target.getInsets();
size.width -= insets.left + insets.right;
size.height -= insets.top + insets.bottom;
int extraX = size.width - width;
int extraY = size.height - height;
int startX = insets.left;
int startY = insets.top;
if (totalX == 0) {
switch (horizontalAlignment) {
case LEFT:
extraX = 0;
break;
case CENTER:
startX += extraX / 2;
extraX = 0;
break;
case RIGHT:
startX += extraX;
extraX = 0;
break;
case FILL:
totalX = nc;
for (int column = 0; column < nc; column++) {
weightX[column] = 1;
}
break;
}
}
if (totalY == 0) {
switch (verticalAlignment) {
case TOP:
extraY = 0;
break;
case CENTER:
startY += extraY / 2;
extraY = 0;
break;
case BOTTOM:
startY += extraY;
extraY = 0;
break;
case FILL:
totalY = nr;
for (int row = 0; row < nr; row++) {
weightY[row] = 1;
}
break;
}
}
int[] xc = new int[nc];
int x = hGap + startX;
for (int column = 0; column < nc; column++) {
xc[column] = x;
if (extraX > 0) {
int pad = (int) Math.round(extraX * weightX[column] / totalX);
widths[column] += pad;
extraX -= pad;
totalX -= weightX[column];
}
x += widths[column] + hGap;
}
int[] yc = new int[nr];
int y = vGap + startY;
for (int row = 0; row < nr; row++) {
yc[row] = y;
if (extraY > 0) {
int pad = (int) Math.round(extraY * weightY[row] / totalY);
heights[row] += pad;
extraY -= pad;
totalY -= weightY[row];
}
y += heights[row] + vGap;
}
for (int i = 0; i < nComponents; i++) {
Component comp = target.getComponent(i);
TableConstraints tc = layoutTable.get(comp);
int column = tc.gridx;
int row = tc.gridy;
int bx = xc[column] + tc.insets.left;
int by = yc[row] + tc.insets.top;
int bw = widths[column];
for (int ix = 1; ix < tc.gridwidth && column + ix < nc; ix++) {
bw += widths[column + ix] + hGap;
}
bw -= tc.insets.left + tc.insets.right;
int bh = heights[row];
for (int iy = 1; iy < tc.gridheight && row + iy < nr; iy++) {
bh += heights[row + iy] + vGap;
}
bh -= tc.insets.top + tc.insets.bottom;
Dimension pSize = (useStrictGridBagModel) ? getMinimumComponentSize(comp) : getPreferredComponentSize(comp);
Rectangle bounds = computeCellBounds(new Rectangle(bx, by, bw, bh), pSize, tc);
comp.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
}
} else {
result = new Dimension(width, height);
}
return result;
}
/* Private method: computeLayoutTable(target) */
/**
* Updates the layoutTable constraints for each component in target
.
* All elements whose gridx
or gridy
constraints
* are RELATIVE
are changed to explicit grid references by
* interpreting the gridwidth
and gridheight
* constraints.
*
* Example: computeLayoutTable(target);
* @param target The target container
*/
private void computeLayoutTable(Container target) {
int[] unfinishedSpans = null;
int row = 0;
int column = 0;
int layoutColumns = nColumns;
int nComponents = target.getComponentCount();
boolean nextEndRow = false;
layoutTable = new HashMap();
for (int i = 0; i < nComponents; i++) {
Component comp = target.getComponent(i);
TableConstraints tc = getConstraints(comp);
if (tc.gridx != GridBagConstraints.RELATIVE) column = tc.gridx;
if (tc.gridy != GridBagConstraints.RELATIVE) row = tc.gridy;
if (nRows > 0 && row >= nRows) {
throw new ErrorException("TableLayout: Too many rows specified");
}
while (unfinishedSpans != null && column < unfinishedSpans.length && unfinishedSpans[column] > 0) {
column++;
if (layoutColumns > 0 && column >= layoutColumns) {
for (int c = 0; c < unfinishedSpans.length; c++) {
if (unfinishedSpans[c] > 0) unfinishedSpans[c]--;
}
row++;
column = getFirstAvailableColumn(unfinishedSpans);
}
}
tc.gridx = column;
tc.gridy = row;
boolean endrow = nextEndRow;
setMaxProperty("width" + column, tc.width);
setMaxProperty("height" + row, tc.height);
setMaxProperty("weightx" + column, tc.weightx);
setMaxProperty("weighty" + row, tc.weighty);
int colspan = 1;
switch (tc.gridwidth) {
case GridBagConstraints.REMAINDER:
endrow = true;
if (layoutColumns > 0) colspan = layoutColumns - column;
break;
case GridBagConstraints.RELATIVE:
if (layoutColumns <= 0) {
throw new ErrorException("TableLayout: Illegal to use gridwidth=RELATIVE in first row");
}
colspan = layoutColumns - column - 1;
nextEndRow = true;
break;
default:
colspan = tc.gridwidth;
endrow = (nColumns > 0 && column + tc.gridwidth >= nColumns);
break;
}
if (colspan > 1 && unfinishedSpans != null) {
for (int c = column; c < Math.min(unfinishedSpans.length, column + colspan); c++) {
if (unfinishedSpans[c] != 0) {
throw new ErrorException("TableLayout: Overlapping cells");
}
}
}
int rowspan = 1;
switch (tc.gridheight) {
case GridBagConstraints.REMAINDER:
rowspan = Integer.MAX_VALUE;
break;
case GridBagConstraints.RELATIVE:
throw new ErrorException("TableLayout: Illegal to use gridheight=RELATIVE");
default:
rowspan = tc.gridheight;
break;
}
if (rowspan > 1) {
if (unfinishedSpans == null) {
unfinishedSpans = new int[column + colspan];
} else if (unfinishedSpans.length < column + colspan) {
int[] oldSpans = unfinishedSpans;
unfinishedSpans = new int[column + colspan];
System.arraycopy(oldSpans, 0, unfinishedSpans, 0, oldSpans.length);
}
for (int c = column; c < column + colspan; c++) {
unfinishedSpans[c] = rowspan;
}
}
tc.gridwidth = colspan;
tc.gridheight = rowspan;
layoutTable.put(comp, tc);
column += colspan;
while (endrow || (layoutColumns > 0 && column >= layoutColumns)) {
if (layoutColumns <= 0) layoutColumns = column;
if (unfinishedSpans != null) {
for (int c = 0; c < unfinishedSpans.length; c++) {
if (unfinishedSpans[c] > 0) unfinishedSpans[c]--;
}
}
row++;
column = getFirstAvailableColumn(unfinishedSpans);
endrow = false;
}
}
if (layoutColumns <= 0) layoutColumns = column;
setIntProperty("nColumns", layoutColumns);
setIntProperty("nRows", row + 1);
}
/* Private method: computeCellBounds(enclosure, psize, tc) */
/**
* Computes the bounds for the cell based on the bounds of the enclosure,
* the preferred size of the object, and the constraints in tc.
*/
private Rectangle computeCellBounds(Rectangle enclosure, Dimension psize, TableConstraints tc) {
int x = enclosure.x;
int y = enclosure.y;
int width = enclosure.width;
int height = enclosure.height;
if (tc.fill == TableConstraints.NONE || tc.fill == TableConstraints.VERTICAL) {
width = psize.width;
}
if (tc.fill == TableConstraints.NONE || tc.fill == TableConstraints.HORIZONTAL) {
height = psize.height;
}
if (width != enclosure.width) {
switch (tc.anchor) {
case TableConstraints.NORTH: case TableConstraints.CENTER: case TableConstraints.SOUTH:
x += (enclosure.width - width) / 2;
break;
case TableConstraints.NORTHEAST: case TableConstraints.EAST: case TableConstraints.SOUTHEAST:
x += enclosure.width - width;
break;
}
}
if (height != enclosure.height) {
switch (tc.anchor) {
case TableConstraints.WEST: case TableConstraints.CENTER: case TableConstraints.EAST:
y += (enclosure.height - height) / 2;
break;
case TableConstraints.SOUTHWEST: case TableConstraints.SOUTH: case TableConstraints.SOUTHEAST:
y += enclosure.height - height;
break;
}
}
return new Rectangle(x, y, width, height);
}
/* Private method: setMaxProperty(key, value) */
/**
* Sets the property for the specified key to the maximum of its
* previous value and the specified new value.
*/
private void setMaxProperty(String key, int value) {
setIntProperty(key, Math.max(value, getIntProperty(key)));
}
/* Private method: setMaxProperty(key, value) */
/**
* Sets the property for the specified key to the maximum of its
* previous value and the specified new value.
*/
private void setMaxProperty(String key, double value) {
setDoubleProperty(key, Math.max(value, getDoubleProperty(key)));
}
/* Private method: setIntProperty(key, value) */
/**
* Sets the property for the specified key to the integer value.
*/
private void setIntProperty(String key, int value) {
propertyTable.put(key, new Integer(value));
}
/* Private method: getIntProperty(key) */
/**
* Gets the integer property associated with the specified key. If no value
* has been set, the method returns 0.
*/
private int getIntProperty(String key) {
Object binding = propertyTable.get(key);
if (binding == null) return 0;
return ((Integer) binding).intValue();
}
/* Private method: setDoubleProperty(key, value) */
/**
* Sets the property for the specified key to the double value.
*/
private void setDoubleProperty(String key, double value) {
propertyTable.put(key, new Double(value));
}
/* Private method: getDoubleProperty(key) */
/**
* Gets the double property associated with the specified key. If no value
* has been set, the method returns 0.
*/
private double getDoubleProperty(String key) {
Object binding = propertyTable.get(key);
if (binding == null) return 0.0;
return ((Double) binding).doubleValue();
}
/* Private method: getFirstAvailableColumn(unfinishedSpans) */
/**
* Gets the first available column in the next row of the table,
* taking into account whether multirow cells are being handled
* as in GridBagLayout.
*/
private int getFirstAvailableColumn(int[] unfinishedSpans) {
if (useStrictGridBagModel && unfinishedSpans != null) {
for (int column = unfinishedSpans.length; column > 0; column--) {
if (unfinishedSpans[column - 1] > 0) return column;
}
}
return 0;
}
/* Private constants */
private static final int LAYOUT_CONTAINER_MODE = 0;
private static final int MINIMUM_LAYOUT_SIZE_MODE = 1;
private static final int PREFERRED_LAYOUT_SIZE_MODE = 2;
private static final int MINIMUM_SCROLLBAR_SIZE = 100;
/* Private instance variables */
private transient Container targetContainer;
private HashMap constraintTable;
private HashMap layoutTable;
private HashMap propertyTable;
private boolean useStrictGridBagModel;
private int nRows;
private int nColumns;
private int horizontalAlignment;
private int verticalAlignment;
private int defaultFill;
private int hGap;
private int vGap;
/* Serial version UID */
/**
* The serialization code for this class. This value should be incremented
* whenever you change the structure of this class in an incompatible way,
* typically by adding a new instance variable.
*/
static final long serialVersionUID = 1L;
}