summaryrefslogtreecommitdiff
path: root/libs/FLib/TableLayout/org
diff options
context:
space:
mode:
Diffstat (limited to 'libs/FLib/TableLayout/org')
-rw-r--r--libs/FLib/TableLayout/org/freixas/tablelayout/TableLayout.java2049
1 files changed, 2049 insertions, 0 deletions
diff --git a/libs/FLib/TableLayout/org/freixas/tablelayout/TableLayout.java b/libs/FLib/TableLayout/org/freixas/tablelayout/TableLayout.java
new file mode 100644
index 0000000..870115d
--- /dev/null
+++ b/libs/FLib/TableLayout/org/freixas/tablelayout/TableLayout.java
@@ -0,0 +1,2049 @@
+//**********************************************************************
+// Package
+//*********************************************************************
+
+package org.freixas.tablelayout;
+
+//**********************************************************************
+// Import list
+//**********************************************************************
+
+import java.awt.*;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * This layout was inspired by the HTML table: it lays out components
+ * much like the HTML table lays out table data. It is as capable as
+ * the GridBagLayout, but much easier to use.
+ * <h3>Attributes</h3>
+ * <p>
+ * When you create a TableLayout, you pass in a String which defines a
+ * set of attributes for the table. The TableLayout is then assigned
+ * to a Container. As you add each Component to the Container, you can
+ * also associate the Component with its own set of attributes.
+ * <p>
+ * The format of the attributes is similar to HTML attributes. The
+ * attributes are case insensitive and can be separated with
+ * any whitespace. Attributes which take a value are followed by an
+ * '=' and then an integer. Here's an example: "cols=6 rgap=2 cgap=5
+ * w".
+ * <p>
+ * Attributes are evaluated from left to right. If you duplicate an
+ * attribute, the right-most one wins.
+ * <p>All attributes can be specified for both the table and the
+ * individual Components. Some attributes are only used by the table,
+ * some are only used by the Components and some are used by both: the
+ * table instance is the default which each Component can override.
+ * <h3>Rows and Columns</h3>
+ * <p>
+ * When you create a table, you will almost always specify the number
+ * of columns in it. The default is 1. Columns are filled from left to
+ * right. When all columns are filled, a new row is automatically
+ * created.
+ * <p>
+ * You can override the default Component placement somewhat with "col"
+ * and "skip". col takes as a value, the column number in which the
+ * Component should be placed. Column numbers begin with 0. The
+ * default is to place the Component in the next available table cell.
+ * One caveat when using col is that if you have already passed the
+ * given column, the layout adds another row and places the Component
+ * in the column on that new row.
+ * <p>
+ * "skip" allows you to skip a number of cells. The default is 0. If
+ * the layout reaches the end of the row, skipping continues on the
+ * next row.
+ * <p>
+ * You can make a Component span multiple rows or columns with rspan
+ * and cspan. The default value for these is 1. Later components will
+ * skip over any occupied cells, which is particularly important to
+ * note for row spanning.
+ * <h3>Spacing</h3>
+ * The space the TableLayout works with is inside the Container's
+ * insets. Some Containers have insets of 0 (e.g. JPanel) and most
+ * don't allow you to alter the insets. Since you will often want to
+ * control the space between the table and the edges of the Container,
+ * an "extra" table inset can be given: titop, tibottom, tileft and
+ * tiright. Each of these takes a value, the pixel offset from the top,
+ * bottom, etc. The default value of each is 0.
+ * <p>
+ * You can create some space between cells in the table. This space is
+ * <em>only</em> placed between cells; never along the edges of the
+ * table. This allows you to nest table layouts and keep consistent
+ * cell spacing. The attributes used are rgap and cgap and their
+ * default value is 0.
+ * <p>
+ * Within a cell, you can also create some space between the cells
+ * edges and the Component in the cell. The attributes are itop,
+ * ibottom, ileft and iright. Their default value is 0.
+ * <h3>Placement and Filling</h3>
+ *<p>
+ * Given that you have something to draw and a space to draw it in,
+ * you have some choices as to where to place it and how to fill it,
+ * particularly when the drawing area is bigger than required.
+ * <p>
+ * Placement attributes allow you to place the item in one of eight
+ * compass directions or centered. The entire table can be placed
+ * within the container using tn, tne, te, tse, ts, tsw, tw, tnw and
+ * tc. Components can be placed within their cell using n, ne, e, se,
+ * s, sw, w, nw and c.
+ * <p>
+ * Fill attributes allow you to fill the item to cover all available
+ * space. Horizontal and vertical filling are handled separately. For
+ * tables, the attributes are tfh, tfv and tf. "tf" fills in both
+ * directions. For Components, use fh, fv and f.
+ * <p>
+ * Placement attributes turn off all filling. Fill attributes turn off
+ * placement, but only in the fill direction. So "n fh" will stretch a
+ * Component horizontally, but will place it at the "north" position
+ * (at the top of the cell).
+ * <p>
+ * The default value for both the table and the individual Components
+ * is to fill in both directions. You will almost always want to
+ * specify your own values.
+ * <h3>Weighting</h3>
+ * <p>
+ * When a table is filled, if the available space exceeds the space
+ * required, we stretch the table to fill the space. This implies that
+ * we have to stretch each cell. How much each cell should be filled
+ * is what weighting is all about. The attributes are rweight and
+ * cweight which take an integer weight factor. The default is 0.
+ * <p>
+ * Note that stretching a cell is not the same as stretching the
+ * Component inside the cell unless the component uses filling.
+ * <p>
+ * If you'd like some simple rules of thumbs, use these:
+ * <ul>
+ * <li>Assign a weight of 1 to rows or columns that you want to
+ * stretch (Also set the "f" attribute for the Components in that row
+ * or column!). Other rows and columns won't.
+ * <li>In some cases, you may want the table to fill while the
+ * components inside it don't. For example, you may want a JPanel's
+ * TitledBorder to fill the available space while the components in
+ * the JPanel stay at their preferred sizes. The solution is to fill
+ * the table, but set the component at the highest row and column
+ * position to have row and column weights of 1. Use "nw" position on
+ * all components in the last row or column. There are alternate
+ * solutions using nested layouts.
+ * </ul>
+ * <p>
+ * Ok, here are the dirty details.
+ * <p>
+ * If the available size is greater than the table's preferred size
+ * and table filling is enabled, weighting is used (they are otherwise
+ * ignored).
+ * <p>
+ * Weights are obtained by looking at each row and column and locating
+ * the largest weight; this becomes the row or column weight. If all
+ * weights are 0, we treat them as though they are all 1. We create
+ * a sum for all row weights and one for all column weights. This
+ * number defines the number of units into which the excess space will
+ * be divided.
+ * <p>
+ * For example, with three column weights of 1, 1, and 1, the space is
+ * divided into 3 units. If the excess space is 30 pixels, each unit
+ * is 10 pixels, which is the extra space each column receives. If the
+ * column weights were 0, 2, and 1, the space is still divided into 3
+ * units. But the column weights specify how many units each column
+ * receives. So, column 0 will receive nothing, column 1, 20 pixels
+ * and column 2, 10 pixels.
+ * <p>
+ * Keep in mind that rows and columns are handled separately. One may
+ * need filling and the other not.
+ * <p>
+ * When we don't have enough space for the preferred row or column
+ * sizes, we ignore the user-defined weights and treat each row or
+ * column as having equal weight. The approach then, is as above
+ * except that we are reducing cell sizes. Another difference is that
+ * no cell will be made smaller than its minimum size.
+ * <h3>Special Spanning Issues</h3>
+ * There are some special issues with row and column spanning. When
+ * determining minimum or preferred sizes, we need to know what
+ * portion of the Component's size to assign to each row and column
+ * that it spans. We solve this by doing two passes. In the first, we
+ * ignore spanning cells and determine row and column sizes without
+ * them. In the second pass, we look to see if the spanning Component
+ * will fit within the row and column sizes we determined. If not, we
+ * currently distribute the extra space based on the row or column
+ * weights of the rows or columns spanned. Someday, we may need to add
+ * attributes to provide more control.
+ * <p>
+ * The row and column weights given are applied to the row or column
+ * in which the Component begins.
+ * <p>
+ * <h3>Summary</h3>
+ * <p>
+ * This table summarizes the attribute information:
+ * <p>
+ * <table border="1" cellpadding="3" cellspacing="0">
+ * <tr bgcolor="#CCCCFF" id="TableHeadingColor">
+ * <td><b>Name</b></td>
+ * <td><b>Description</b></td>
+ * <td><b>Has Value?</b></td>
+ * <td><b>Default</b></td>
+ * <td><b>Scope</b></td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>cols</td>
+ * <td>Number of columns</td>
+ * <td>Yes</td>
+ * <td>1</td>
+ * <td>Table</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>col</td>
+ * <td>Place Component in this column</td>
+ * <td>Yes</td>
+ * <td>Next empty column</td>
+ * <td>Component</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>skip</td>
+ * <td>Skip a number of columns</td>
+ * <td>Yes</td>
+ * <td>0</td>
+ * <td>Component</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>rspan, cspan</td>
+ * <td>Row and column spanning</td>
+ * <td>Yes</td>
+ * <td>1</td>
+ * <td>Component</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>titop, tibottom, tileft, tiright</td>
+ * <td>Table insets</td>
+ * <td>Yes</td>
+ * <td>0</td>
+ * <td>Table</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>rgap, cgap</td>
+ * <td>Row and column gaps</td>
+ * <td>Yes</td>
+ * <td>0</td>
+ * <td>Table</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>itop, ibottom, ileft, iright</td>
+ * <td>Component insets</td>
+ * <td>Yes</td>
+ * <td>0</td>
+ * <td>Table/Component</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>tn, tne, te, tse, ts, tsw, tw, tnw, tc, tf, tfh, tfv</td>
+ * <td>Table placement and fill</td>
+ * <td>No</td>
+ * <td>tf</td>
+ * <td>Table</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>n, ne, e, se, s, sw, w, nw, c, f, fh, fv</td>
+ * <td>Component placement and fill</td>
+ * <td>No</td>
+ * <td>f</td>
+ * <td>Table/Component</td>
+ * </tr>
+ * <tr bgcolor="white">
+ * <td>rweight, cweight</td>
+ * <td>Row and column weights</td>
+ * <td>Yes</td>
+ * <td>0</td>
+ * <td>Table/Component</td>
+ * </tr>
+ * </table>
+ *
+ * @author Antonio Freixas
+ */
+
+// Copyright © 2000-2004 Credence Systems Corporation.
+// All Rights Reserved.
+
+public class TableLayout
+ implements LayoutManager2
+{
+
+//**********************************************************************
+// Private Members
+//**********************************************************************
+
+// This is the set of attributes applied to the table. Some attributes
+// are used as cell defaults. Cell-only attributes are ignored
+
+private Attributes tableAttributes;
+
+// Attributes for each component can be found in these hash tables
+
+private HashMap compAttributes = new HashMap();
+
+// These variables store information about the row/col arrangement of
+// the components. These are set by placeComponents()
+
+private int nRows = 0;
+private int nCols = 0;
+private Component[][] components = null;
+
+// We cache measureComponents() information so that it is recalculated
+// after invalidateLayout() is called
+
+private boolean useCacheMeasureResults = false;
+
+// These variables store sizing information set by measureComponents()
+
+private int[] minWidth;
+private int[] prefWidth;
+private int[] maxWidth;
+private int[] adjWidth;
+private int[] colWeight;
+
+private int[] minHeight;
+private int[] prefHeight;
+private int[] maxHeight;
+private int[] adjHeight;
+private int[] rowWeight;
+
+// These sizes are the minimum width for the table, not including
+// either the container's insets or the table's insets
+
+private int MinWidth = 0;
+private int MinHeight = 0;
+private int PrefWidth = 0;
+private int PrefHeight = 0;
+private int MaxWidth = 0;
+private int MaxHeight = 0;
+private int ColWeight = 0;
+private int RowWeight = 0;
+
+static private int classCount = 0;
+private int instanceCount = classCount++;
+
+//**********************************************************************
+// Constructors
+//**********************************************************************
+
+/**
+ * Construct a new TableLayout.
+ */
+
+public
+TableLayout()
+{
+ this(null);
+}
+
+/**
+ * Construct a new TableLayout with the given attributes.
+ *
+ * @param attributes A list of attributes for the table. The list is
+ * described in the class documentation above. Cell-only
+ * attributes are ignored.
+ */
+
+public
+TableLayout(
+ String attributes)
+{
+ tableAttributes = new Attributes(attributes);
+}
+
+//**********************************************************************
+// Public
+//**********************************************************************
+
+/**
+ * Reset the table attributes for the layout.
+ *
+ * @param attributes The new table attributes.
+ */
+
+public void
+setTableAttributes(
+ String attributes)
+{
+ // Set the attributes for the table
+
+ tableAttributes = new Attributes(attributes);
+
+ // Since the component attributes "inherit" from the table
+ // attributes, any change to the table attributes causes us to
+ // reprocess all existing component attributes
+
+ Iterator iter = compAttributes.keySet().iterator();
+ while (iter.hasNext()) {
+ Component comp = (Component)iter.next();
+ Attributes a = (Attributes)compAttributes.get(comp);
+ a.parse(); // Re-parse
+ }
+
+ components = null;
+ useCacheMeasureResults = false;
+}
+
+/**
+ * Reset the attributes for a component in the layout. The component
+ * must already have been added to the container or else this call has
+ * no effect.
+ *
+ * @param comp The component to alter.
+ * @param attributes The new attributes for the component.
+ */
+
+public void
+setAttributes(
+ Component comp,
+ String attributes)
+{
+ if (compAttributes.get(comp) != null) {
+ Attributes a = new Attributes(attributes, false);
+ compAttributes.put(comp, a);
+
+ components = null;
+ useCacheMeasureResults = false;
+ }
+}
+
+/**
+ * Adds the component with the specified attributes to the layout.
+ *
+ * @param attributes A list of attributes for the component. The list
+ * is described in the class documentation above. Table-only
+ * attributes are ignored.
+ * @param comp The component to be added.
+ */
+
+public void
+addLayoutComponent(
+ String attributes,
+ Component comp)
+{
+ Attributes a = new Attributes(attributes, false);
+ compAttributes.put(comp, a);
+
+ components = null;
+ useCacheMeasureResults = false;
+
+ // DEBUG
+
+// if ("DEBUG".equals(comp.getName())) {
+// System.out.println(
+// "Adding comp " + comp.getClass().getName() + " " + a);
+// }
+}
+
+/**
+ * Adds the specified component to the layout, using the specified
+ * constraint object (which we expect to be a String of attributes).
+ *
+ * @param comp The component to be added.
+ * @param constraints A list of attributes for the component. The list
+ * is described in the class documentation above. Table-only
+ * attributes are ignored.
+ */
+
+public void
+addLayoutComponent(
+ Component comp,
+ Object constraints)
+{
+ String attributes = "";
+ if (constraints instanceof String) {
+ attributes = (String)constraints;
+ }
+ addLayoutComponent(attributes, comp);
+}
+
+/**
+ * Removes the specified component from the layout.
+
+ * @param comp The component to be removed.
+ */
+
+public void
+removeLayoutComponent(
+ Component comp)
+{
+ compAttributes.remove(comp);
+ components = null;
+}
+
+/**
+ * Calculates the minimum size dimensions for the layout given the
+ * components in the a parent container.
+ *
+ * @param parent The container to be laid out.
+ * @return The minimum layout size.
+ * @see #preferredLayoutSize(Container)
+ * @see #maximumLayoutSize(Container)
+ */
+
+public Dimension
+minimumLayoutSize(
+ Container parent)
+{
+ Insets insets = parent.getInsets();
+ measureComponents(parent);
+ int w =
+ insets.left + insets.right +
+ tableAttributes.tableInsets.left +
+ tableAttributes.tableInsets.right +
+ MinWidth;
+ int h =
+ insets.top + insets.bottom +
+ tableAttributes.tableInsets.top +
+ tableAttributes.tableInsets.bottom +
+ MinHeight;
+ if (w > Short.MAX_VALUE) w = Short.MAX_VALUE;
+ if (h > Short.MAX_VALUE) h = Short.MAX_VALUE;
+
+ return new Dimension(w, h);
+}
+
+/**
+ * Calculates the preferred size dimensions for the layout given the
+ * components in a parent container.
+ *
+ * @param parent The container to be laid out.
+ * @return The preferred layout size.
+ * @see #minimumLayoutSize(Container)
+ * @see #maximumLayoutSize(Container)
+ */
+
+public Dimension
+preferredLayoutSize(
+ Container parent)
+{
+ Insets insets = parent.getInsets();
+ measureComponents(parent);
+ int w =
+ insets.left + insets.right +
+ tableAttributes.tableInsets.left +
+ tableAttributes.tableInsets.right +
+ PrefWidth;
+ int h =
+ insets.top + insets.bottom +
+ tableAttributes.tableInsets.top +
+ tableAttributes.tableInsets.bottom +
+ PrefHeight;
+ if (w > Short.MAX_VALUE) w = Short.MAX_VALUE;
+ if (h > Short.MAX_VALUE) h = Short.MAX_VALUE;
+
+ return new Dimension(w, h);
+}
+
+/**
+ * Calculates the maximum size dimensions for the layout given the
+ * components in a parent container.
+ *
+ * @param parent The container parent.
+ * @return The maximum layout size.
+ * @see #minimumLayoutSize(Container)
+ * @see #preferredLayoutSize(Container)
+ */
+
+public Dimension
+maximumLayoutSize(
+ Container parent)
+{
+ Insets insets = parent.getInsets();
+ measureComponents(parent);
+
+ // Note that the maximum size of the container is not the maximum
+ // size of the table if the fill options are not used
+
+ int w = Short.MAX_VALUE;
+ if (tableAttributes.tableHorizontal != Attributes.FILL) {
+ w = insets.left + insets.right +
+ tableAttributes.tableInsets.left +
+ tableAttributes.tableInsets.right +
+ MaxWidth;
+ if (w > Short.MAX_VALUE) w = Short.MAX_VALUE;
+ }
+
+ int h = Short.MAX_VALUE;
+ if (tableAttributes.tableVertical != Attributes.FILL) {
+ h = insets.top + insets.bottom +
+ tableAttributes.tableInsets.top +
+ tableAttributes.tableInsets.bottom +
+ MaxHeight;
+ if (h > Short.MAX_VALUE) h = Short.MAX_VALUE;
+ }
+
+ return new Dimension(w, h);
+}
+
+/**
+ * Returns the alignment along the x axis. This always returns 0.5.
+ *
+ * @param parent The container whose alignment we want.
+ * @return The alignment along the x axis.
+ */
+
+public float
+getLayoutAlignmentX(
+ Container parent)
+{
+ return 0.5f;
+}
+
+/**
+ * Returns the alignment along the y axis. This always returns 0.5.
+ *
+ * @param parent The container whose alignment we want.
+ * @return The alignment along the y axis.
+ */
+
+public float
+getLayoutAlignmentY(
+ Container parent)
+{
+ return 0.5f;
+}
+
+/**
+ * Invalidates the layout. Cached information will be discarded.
+ *
+ * @param parent The container whose alignment we want.
+ */
+
+public void
+invalidateLayout(
+ Container parent)
+{
+ useCacheMeasureResults = false;
+}
+
+/**
+ * Lays out the components in the given container.
+ *
+ * @param parent The container which needs to be laid out.
+ */
+
+public void
+layoutContainer(
+ Container parent)
+{
+ // Get the row and column measurements
+
+ measureComponents(parent);
+
+ // Get the parent insets and determine the full amount of space we
+ // have available
+
+ Insets insets = parent.getInsets();
+ int fullWidth =
+ parent.getSize().width -
+ (insets.left + insets.right) -
+ (tableAttributes.tableInsets.left +
+ tableAttributes.tableInsets.right);
+ int fullHeight = parent.getSize().height -
+ (insets.top + insets.bottom) -
+ (tableAttributes.tableInsets.top +
+ tableAttributes.tableInsets.bottom);
+
+ // We normally draw each row and column in its preferred size. If
+ // we have more space, we grow the cells. If less, we shrink the
+ // cells
+
+ boolean shrinkWidth = fullWidth < PrefWidth;
+ boolean shrinkHeight = fullHeight < PrefHeight;
+
+ // Get the position and size of the table. There are three
+ // possibilities for the table size:
+ //
+ // * The available space equals or exceeds the preferred size
+ // and the table is filled - use the full space available. We
+ // will expand the cells in a later step.
+ //
+ // * The available space equals or exceeds the preferred size
+ // and the table is not filled - use the preferred size.
+ //
+ // * The available space is less than the preferred size - use
+ // the available space. We will shrink the cells in a later
+ // step.
+
+ int tableX = insets.left + tableAttributes.tableInsets.left;
+ int tableY = insets.top + tableAttributes.tableInsets.top;
+
+ int tableWidth = PrefWidth;
+ if (shrinkWidth ||
+ tableAttributes.tableHorizontal == Attributes.FILL) {
+ tableWidth = fullWidth;
+ }
+ int tableHeight = PrefHeight;
+ if (shrinkHeight ||
+ tableAttributes.tableVertical == Attributes.FILL) {
+ tableHeight = fullHeight;
+ }
+
+ if (tableAttributes.tableHorizontal == Attributes.CENTER ||
+ tableAttributes.tableHorizontal == Attributes.FILL) {
+ tableX += (fullWidth - tableWidth) / 2;
+ }
+ else if (tableAttributes.tableHorizontal == Attributes.RIGHT) {
+ tableX += fullWidth - tableWidth;
+ }
+
+ if (tableAttributes.tableVertical == Attributes.CENTER ||
+ tableAttributes.tableVertical == Attributes.FILL) {
+ tableY += (fullHeight - tableHeight) / 2;
+ }
+ else if (tableAttributes.tableVertical == Attributes.BOTTOM) {
+ tableY += fullHeight - tableHeight;
+ }
+
+ // Now adjust the column and row cell sizes
+
+ adjustCellSizes(
+ nCols,
+ (tableAttributes.tableHorizontal == Attributes.FILL),
+ shrinkWidth,
+ minWidth,
+ PrefWidth, prefWidth,
+ fullWidth,
+ ColWeight, colWeight,
+ adjWidth,
+ parent);
+
+ adjustCellSizes(
+ nRows,
+ (tableAttributes.tableVertical == Attributes.FILL),
+ shrinkHeight,
+ minHeight,
+ PrefHeight, prefHeight,
+ fullHeight,
+ RowWeight, rowWeight,
+ adjHeight,
+ parent);
+
+ // Begin the component layout loop
+
+ for (int r = 0; r < nRows; r++) {
+ for (int c = 0; c < nCols; c++) {
+ Component comp = components[r][c];
+ if (comp == null) continue;
+
+ Attributes attributes = (Attributes)compAttributes.get(comp);
+
+ Dimension compMinSize = comp.getMinimumSize();
+ Dimension compPrefSize = comp.getPreferredSize();
+ Dimension compMaxSize = comp.getMaximumSize();
+
+ // Base position
+
+ int compX =
+ tableX +
+ (tableAttributes.cGap * c) +
+ attributes.cellInsets.left;
+ for (int i = 0; i < c; i++) compX += adjWidth[i];
+
+ int compY =
+ tableY +
+ (tableAttributes.rGap * r) +
+ attributes.cellInsets.top;
+ for (int i = 0; i < r; i++) compY += adjHeight[i];
+
+ // Get the cell size. This has to take into account row
+ // and column spanning
+
+ int cellWidth = adjWidth[c];
+ for (int i = 1; i < attributes.cSpan; i++) {
+ cellWidth += tableAttributes.cGap + adjWidth[c + i];
+ }
+
+ int cellHeight = adjHeight[r];
+ for (int i = 1; i < attributes.rSpan; i++) {
+ cellHeight += tableAttributes.rGap + adjHeight[r + i];
+ }
+
+ int insetCellWidth =
+ cellWidth -
+ attributes.cellInsets.left - attributes.cellInsets.right;
+ int insetCellHeight =
+ cellHeight -
+ attributes.cellInsets.top - attributes.cellInsets.bottom;
+
+ // Get the component size. Use the preferred size, if
+ // possible. If not use the cell size minus insets
+
+ int compWidth = compPrefSize.width;
+ int compHeight = compPrefSize.height;
+ if (compWidth > insetCellWidth) compWidth = insetCellWidth;
+ if (compHeight > insetCellHeight) compHeight = insetCellHeight;
+
+ // Adjust for fill
+
+ if (attributes.horizontal == Attributes.FILL) {
+ compWidth = insetCellWidth;
+ compWidth = Math.max(compWidth, compMinSize.width);
+ // Some components (like JButton) can exceed their max size
+ // compWidth = Math.min(compWidth, compMaxSize.width);
+ }
+ if (attributes.vertical == Attributes.FILL) {
+ compHeight = insetCellHeight;
+ compHeight = Math.max(compHeight, compMinSize.height);
+ // Some components (like JButton) can exceed their max size
+ // compHeight = Math.min(compHeight, compMaxSize.height);
+ }
+
+ // Position properly. We treat FILL like CENTER since
+ // the min/max limits may have prevented us from really
+ // filling
+
+ if (attributes.horizontal == Attributes.CENTER ||
+ attributes.horizontal == Attributes.FILL) {
+ compX += (cellWidth -
+ (attributes.cellInsets.left +
+ attributes.cellInsets.right) - compWidth) / 2;
+ }
+ else if (attributes.horizontal == Attributes.RIGHT) {
+ compX += (cellWidth -
+ (attributes.cellInsets.left +
+ attributes.cellInsets.right) - compWidth);
+ }
+
+ if (attributes.vertical == Attributes.CENTER ||
+ attributes.vertical == Attributes.FILL) {
+ compY += (cellHeight -
+ (attributes.cellInsets.top +
+ attributes.cellInsets.bottom) - compHeight) / 2;
+ }
+ else if (attributes.vertical == Attributes.BOTTOM) {
+ compY += (cellHeight -
+ (attributes.cellInsets.top +
+ attributes.cellInsets.bottom) - compHeight);
+ }
+
+ // Place the component
+
+ comp.setBounds(compX, compY, compWidth, compHeight);
+
+ // DEBUG
+
+// if ("DEBUG".equals(parent.getName())) {
+// System.out.println("Placing component " +
+// comp.getClass().getName() +
+// " (" + compX + ", " + compY + ") " +
+// compWidth + " x " + compHeight);
+// }
+ }
+ }
+}
+
+// The inherited toString() method is acceptable.
+
+//**********************************************************************
+// Private
+//**********************************************************************
+
+/**
+ * The row or column sizes need to be adjusted. We may want to grow
+ * or shrink the sizes, based on whether the available space is larger
+ * or smaller than the preferred size.
+ * <p>
+ * If we grow the table, we pay attention to the user's weighting
+ * factors. If we shrink the table, we assign all cells a weight
+ * factor of 1.
+ *
+ * @param nCells The number of cells in the row or column.
+ * @param fill True if the table rows or columns should fill the
+ * available space.
+ * @param shrink True if the available size is less than the preferred
+ * size.
+ * @param minSize The minimum sizes of each row or column.
+ * @param PrefSize The sum of the preferred sizes of all cells in the
+ * row or column plus any cell gaps.
+ * @param prefSize The preferred sizes of each row or column.
+ * @param CellWeight The sum of all cell weights in the row or column.
+ * @param cellWeight The weight of each row or column.
+ * @param adjSize The adjusted size of each row or column. The
+ * contents of this array are set and returned.
+ */
+
+private void
+adjustCellSizes(
+ int nCells,
+ boolean fill,
+ boolean shrink,
+ int[] minSize,
+ int PrefSize,
+ int[] prefSize,
+ int fullSize,
+ int CellWeight,
+ int[] cellWeight,
+ int[] adjSize,
+ Container parent)
+
+{
+ // The sum of the weights (CellWeight) determines how many units
+ // any excess (or reduced) space should be divided into. The
+ // unitOfSpace variable is the size of each unit.
+ //
+ // We use weighting under two conditions:
+ //
+ // * We are shrinking the table.
+ //
+ // * We have more space than we need and the user asked us to
+ // fill the available space
+ //
+ // When we have more than enough space for the preferred row and
+ // column sizes, we follow the user's weighting. There is a
+ // special case if all weights are 0: the weights are treated as
+ // thought they were all 1.
+ //
+ // When do not have enough space, we weight everything the same.
+
+ double unitOfSpace = 0.0;
+ if (shrink || fill) {
+ unitOfSpace =
+ (double)(fullSize - PrefSize) /
+ (double)((shrink || CellWeight == 0) ? nCells : CellWeight);
+ }
+ else {
+ // No adjustment needed: use the preferred sizes
+
+ for (int i = 0; i < nCells; i++) {
+ adjSize[i] = prefSize[i];
+ }
+ return;
+ }
+
+ double extraSpace; int iExtraSpace;
+ double error = 0.0; int iError = -999999;
+
+ int adjWeight;
+
+ for (int i = 0; i < nCells; i++) {
+
+ // Initialize the adjusted size to the preferred size
+
+ adjSize[i] = prefSize[i];
+
+ // Get the cell weight based on various conditions
+
+ adjWeight = (shrink || CellWeight == 0) ? 1 : cellWeight[i];
+
+ // Determine how much extra space to give each cell. The space
+ // is the weight (number of units) times the unit size. We can
+ // only assign an integer number of pixels, which creates a
+ // fractional error
+
+ extraSpace = unitOfSpace * adjWeight;
+ iExtraSpace = (int)extraSpace;
+ adjSize[i] += iExtraSpace;
+
+ error += extraSpace - iExtraSpace;
+ iError = (int)error;
+
+ // Increment/decrement this cell by the accumulated integer
+ // error, if it's not 0
+
+ if (shrink) {
+ if (iError < 0) {
+ adjSize[i] += iError;
+ }
+ }
+ else {
+ if (iError > 0) {
+ adjSize[i] += iError;
+ }
+ }
+ error -= iError;
+
+ // If we're shrinking, we need to prevent any cell from
+ // shrinking below its minimum size. The error is adjusted to
+ // include the space added to the cell
+
+ if (shrink && (adjSize[i] < minSize[i])) {
+ error -= minSize[i] - adjSize[i];
+ adjSize[i] = minSize[i];
+ }
+
+ iError = (int)error;
+ }
+
+ // If we are growing, the error should be less than 1 pixel. If we
+ // are shrinking, we limit each cell to its minimum size, so we
+ // can accumulate larger errors as cells refuse to shrink. So we
+ // distribute the error to cells that can still shrink. We repeat
+ // this until we've reduced the error to 0 or we're unable to
+ // shrink the error anymore.
+ //
+ // Remember that iError is a negative number
+
+ if (shrink || iError < 0) {
+ int lastIError;
+
+ do {
+ lastIError = iError;
+ for (int i = 0; i < nCells; i++) {
+
+ // If the cell is already at its minimum size, skip it
+
+ if (adjSize[i] > minSize[i]) {
+ adjSize[i]--;
+ iError++;
+ }
+ }
+ }
+ while (iError < 0 && iError > lastIError);
+ }
+
+ // DEBUG
+
+// if ("DEBUG".equals(parent.getName())) {
+// System.out.println(" iError " + iError);
+// for (int i = 0; i < nCells; i++) {
+// System.out.println(" " + i + ") Adjusted size = " + adjSize[i]);
+// }
+// }
+}
+
+/**
+ * For each component, determine its row/col position and place it in
+ * an array for easy access later. Elements spanning multiple rows
+ * and/or columns are placed in the NW row/col slot in the array.
+ * Results are placed in class fields.
+ *
+ * @param parent The parent container.
+ */
+
+private void
+placeComponents(
+ Container parent)
+{
+ // If we haven't added or removed a component since the last time
+ // placeComponents() was called, we assume the current results are
+ // OK
+
+ if (components != null) return;
+
+ int compCount = parent.getComponentCount();
+
+ // Get the number of columns specified by the user
+
+ nCols = tableAttributes.columns;
+
+ // Create the array of components
+
+ CompArray compArray = new CompArray(tableAttributes.columns, compCount);
+
+ // Fill the array with components, taking row/column spanning
+ // into account
+
+ int row = 0;
+ int col = 0;
+
+ for (int i = 0; i < compCount; i++) {
+
+ // Get the next component and its options
+
+ Component comp = parent.getComponent(i);
+ Attributes attributes = (Attributes)compAttributes.get(comp);
+
+ // If the column span is greater than the column size,
+ // truncate it to the column size
+
+ attributes.cSpan = attributes.originalCSpan;
+ if (attributes.cSpan > tableAttributes.columns) {
+ attributes.cSpan = tableAttributes.columns;
+
+ }
+ // Handle options to force us to column 0 or to skip columns
+
+ if (attributes.column != Attributes.NEXT_COLUMN) {
+ if (col > attributes.column) row++;
+ col = attributes.column;
+ }
+ col += attributes.skip;
+ if (col >= nCols) {
+ row++;
+ col = 0;
+ }
+
+ // Skip over any cells that are already occupied
+
+ while (compArray.get(row, col) != null) {
+ col++;
+ if (col >= nCols) {
+ row++;
+ col = 0;
+ }
+ }
+
+ // If spanning multiple columns, will we fit on this row?
+
+ if (col + attributes.cSpan > nCols) {
+ row++;
+ col = 0;
+ }
+
+ // For now, fill all the cells that are occupied by this
+ // component
+
+ for (int c = 0; c < attributes.cSpan; c++) {
+ for (int r = 0; r < attributes.rSpan; r++) {
+ compArray.set(row + r, col + c, comp);
+ }
+ }
+
+ // Advance to the next cell, ready for the next component
+
+ col += attributes.cSpan;
+ if (col >= nCols) {
+ row++;
+ col = 0;
+ }
+ }
+
+ // Now we know how many rows there are. We can use a normal,
+ // properly sized array from now on. The array returned includes
+ // the maximum row into which anything was entered, including any
+ // row spans
+
+ components = compArray.getArray();
+ nRows = components.length;
+
+ // Now we've positioned our components we can thin out the cells so
+ // we only remember the top left corner of each component
+
+ for (row = 0; row < nRows; row++) {
+ for (col = 0; col < nCols; col++) {
+ Component comp = components[row][col];
+ for (int r = row; r < nRows && components[r][col] == comp; r++) {
+ for (int c = col; c < nCols && components[r][c] == comp; c++) {
+ if (r > row || c > col) {
+ components[r][c] = null;
+ }
+ }
+ }
+ }
+ }
+
+ // DEBUG
+
+// if ("DEBUG".equals(parent.getName())) {
+// System.out.println("placeComponents finished: rows = " +
+// nRows + " cols = " + nCols);
+// for (int r = 0; r < nRows; r++) {
+// System.out.println("Row " + r + ":");
+// for (int c = 0; c < nCols; c++) {
+// System.out.println(" Col " + c + " Comp " +
+// ((components[r][c] == null) ?
+// "none" :
+// components[r][c].getClass().getName()));
+// }
+// }
+// }
+}
+
+/**
+ * In this method, we will determine the minimum, preferred and
+ * maximum sizes of the components as layed out by the table layout
+ * manager
+ *
+ * @param parent The parent container.
+ */
+
+private void
+measureComponents(
+ Container parent)
+{
+ if (useCacheMeasureResults) return;
+
+ // Determine the row/col positions for the components
+
+ placeComponents(parent);
+
+ // Allocate new arrays to store row and column preferred and min
+ // sizes, but only if the old arrays aren't big enough
+
+ if (minWidth == null || minWidth.length < nCols) {
+ minWidth = new int[nCols];
+ prefWidth = new int[nCols];
+ maxWidth = new int[nCols];
+ adjWidth = new int[nCols];
+ colWeight = new int[nCols];
+ }
+ if (minHeight == null || minHeight.length < nRows) {
+ minHeight = new int[nRows];
+ prefHeight = new int[nRows];
+ maxHeight = new int[nRows];
+ adjHeight = new int[nRows];
+ rowWeight = new int[nRows];
+ }
+
+ for (int i = 0; i < nCols; i++) {
+ minWidth[i] = 0;
+ prefWidth[i] = 0;
+ maxWidth[i] = 0;
+ colWeight[i] = 0;
+ }
+ for (int i = 0; i < nRows; i++) {
+ minHeight[i] = 0;
+ prefHeight[i] = 0;
+ maxHeight[i] = 0;
+ rowWeight[i] = 0;
+ }
+
+ // Measure the minimum and preferred size of each row and column
+
+ for (int row = 0; row < nRows; row++) {
+ for (int col = 0; col < nCols; col++) {
+ Component comp = components[row][col];
+ if (comp != null) {
+ Attributes attributes = (Attributes)compAttributes.get(comp);
+
+ Dimension minSize = new Dimension(comp.getMinimumSize());
+ Dimension prefSize = new Dimension(comp.getPreferredSize());
+ Dimension maxSize = new Dimension(comp.getMaximumSize());
+
+ // Add the cell insets
+
+ minSize.width +=
+ attributes.cellInsets.left + attributes.cellInsets.right;
+ minSize.height +=
+ attributes.cellInsets.top + attributes.cellInsets.bottom;
+ prefSize.width +=
+ attributes.cellInsets.left + attributes.cellInsets.right;
+ prefSize.height +=
+ attributes.cellInsets.top + attributes.cellInsets.bottom;
+ maxSize.width +=
+ attributes.cellInsets.left + attributes.cellInsets.right;
+ maxSize.height +=
+ attributes.cellInsets.right + attributes.cellInsets.bottom;
+
+ // Make sure that 0 <= minSize <= prefSize <= maxSize
+
+ limitDimension(minSize, new Dimension(0, 0));
+ limitDimension(prefSize, minSize);
+ limitDimension(maxSize, prefSize);
+
+ // First pass, we determine the sizes while ignoring
+ // components which span columns or rows
+
+ if (attributes.cSpan == 1) {
+ minWidth[col] =
+ Math.max(minSize.width, minWidth[col]);
+ prefWidth[col] =
+ Math.max(prefSize.width, prefWidth[col]);
+ maxWidth[col] =
+ Math.max(maxSize.width, maxWidth[col]);
+ }
+
+ if (attributes.rSpan == 1) {
+ minHeight[row] =
+ Math.max(minSize.height, minHeight[row]);
+ prefHeight[row] =
+ Math.max(prefSize.height, prefHeight[row]);
+ maxHeight[row] =
+ Math.max(maxSize.height, maxHeight[row]);
+ }
+
+ // Get the row and column weights. The weight is the
+ // maximum value for the row or column
+
+ if (attributes.cWeight > colWeight[col]) {
+ colWeight[col] = attributes.cWeight;
+ }
+ if (attributes.rWeight > rowWeight[row]) {
+ rowWeight[row] = attributes.rWeight;
+ }
+ }
+ }
+ }
+
+ // Do it again, but just for components which span multiple cells.
+ //
+
+ for (int row = 0; row < nRows; row++) {
+ for (int col = 0; col < nCols; col++) {
+ Component comp = components[row][col];
+ if (comp != null) {
+ Attributes attributes = (Attributes)compAttributes.get(comp);
+
+ if (attributes.rSpan == 1 && attributes.cSpan == 1) continue;
+
+ Dimension minSize = new Dimension(comp.getMinimumSize());
+ Dimension prefSize = new Dimension(comp.getPreferredSize());
+ Dimension maxSize = new Dimension(comp.getMaximumSize());
+
+ // Add the cell insets
+
+ minSize.width +=
+ attributes.cellInsets.top + attributes.cellInsets.bottom;
+ minSize.height +=
+ attributes.cellInsets.left + attributes.cellInsets.right;
+ prefSize.width +=
+ attributes.cellInsets.top + attributes.cellInsets.bottom;
+ prefSize.height +=
+ attributes.cellInsets.left + attributes.cellInsets.right;
+ maxSize.width +=
+ attributes.cellInsets.top + attributes.cellInsets.bottom;
+ maxSize.height +=
+ attributes.cellInsets.left + attributes.cellInsets.right;
+
+ // Make sure that 0 <= minSize <= prefSize <= maxSize
+
+ limitDimension(minSize, new Dimension(0, 0));
+ limitDimension(prefSize, minSize);
+ limitDimension(maxSize, prefSize);
+
+ if (attributes.cSpan > 1) {
+ adjustForSpans(col,
+ minSize.width,
+ minWidth,
+ colWeight,
+ attributes.cSpan,
+ tableAttributes.cGap);
+ adjustForSpans(col,
+ prefSize.width,
+ prefWidth,
+ colWeight,
+ attributes.cSpan,
+ tableAttributes.cGap);
+ adjustForSpans(col,
+ maxSize.width,
+ maxWidth,
+ colWeight,
+ attributes.cSpan,
+ tableAttributes.cGap);
+ }
+
+ if (attributes.rSpan > 1) {
+ adjustForSpans(row,
+ minSize.height,
+ minHeight,
+ rowWeight,
+ attributes.rSpan,
+ tableAttributes.rGap);
+ adjustForSpans(row,
+ prefSize.height,
+ prefHeight,
+ rowWeight,
+ attributes.rSpan,
+ tableAttributes.rGap);
+ adjustForSpans(row,
+ maxSize.height,
+ maxHeight,
+ rowWeight,
+ attributes.rSpan,
+ tableAttributes.rGap);
+ }
+ }
+ }
+ }
+
+ // Add up all the individual values
+
+ MinWidth = 0;
+ MinHeight = 0;
+ PrefWidth = 0;
+ PrefHeight = 0;
+ MaxWidth = 0;
+ MaxHeight = 0;
+ ColWeight = 0;
+ RowWeight = 0;
+
+ // Sum up everything
+
+ for (int i = 0; i < nCols; i++) {
+ MinWidth += minWidth[i];
+ PrefWidth += prefWidth[i];
+ MaxWidth += maxWidth[i];
+ ColWeight += colWeight[i];
+ }
+
+ for (int i = 0; i < nRows; i++) {
+ MinHeight += minHeight[i];
+ PrefHeight += prefHeight[i];
+ MaxHeight += maxHeight[i];
+ RowWeight += rowWeight[i];
+ }
+
+ // Add in the table gaps
+
+ int cExtra = tableAttributes.cGap * (nCols - 1);
+ int rExtra = tableAttributes.rGap * (nRows - 1);
+
+ MinWidth += cExtra;
+ PrefWidth += cExtra;
+ MaxWidth += cExtra;
+
+ MinHeight += rExtra;
+ PrefHeight += rExtra;
+ MaxHeight += rExtra;
+
+ // DEBUG
+
+// if ("DEBUG".equals(parent.getName())) {
+// System.out.println("MeasureComponents:");
+// System.out.println(" Min " + MinWidth + ", " + MinHeight);
+// System.out.println(" Pref " + PrefWidth + ", " + PrefHeight);
+// System.out.println(" Max " + MaxWidth + ", " + MaxHeight);
+// System.out.println(" Weight " + ColWeight + ", " + RowWeight );
+
+// for (int c = 0; c < nCols; c++) {
+// System.out.println(" Col " + c +
+// " min " + minWidth[c] +
+// " pref " + prefWidth[c] +
+// " max " + maxWidth[c] +
+// " wgt " + colWeight[c]);
+// }
+// for (int r = 0; r < nRows; r++) {
+// System.out.println(" Row " + r +
+// " min " + minHeight[r] +
+// " pref " + prefHeight[r] +
+// " max " + maxHeight[r] +
+// " wgt " + rowWeight[r]);
+// }
+// }
+
+ // We keep using these results until the layout is invalidated
+
+ useCacheMeasureResults = true;
+}
+
+/**
+ * Make sure the first dimension is greater than or equal to the
+ * second. Also make sure the first dimension is less than an absolute
+ * maximum.
+ *
+ * @param d1 The first dimension (may be modified).
+ * @param d2 The second dimension (will not be modified).
+ */
+
+private void
+limitDimension(
+ Dimension d1,
+ Dimension d2)
+{
+ if (d1.width < d2.width) d1.width = d2.width;
+ if (d1.height < d2.height) d1.height = d2.height;
+ if (d1.width > Short.MAX_VALUE) d1.width = Short.MAX_VALUE;
+ if (d1.height > Short.MAX_VALUE) d1.height = Short.MAX_VALUE;
+}
+
+/**
+ * If a component spans multiple rows or columns, we need to
+ * distribute portions of its size to the individual rows and columns.
+ *
+ * @param pos Row or column position where the span component starts.
+ * @param compSize The height or width of the component
+ * @param sizes The array of widths or heights to adjust.
+ * @param span The number of cells spanned by the component.
+ * @param gap The row or column gap.
+ */
+
+private void
+adjustForSpans(
+ int pos,
+ int compSize,
+ int[] sizes,
+ int[] weight,
+ int span,
+ int gap)
+{
+ // The total size is the size of the rows or columns plus all the
+ // space in between
+
+ int totalSize = 0;
+ for (int i = 0; i < span; i++) {
+ totalSize += sizes[pos + i];
+ }
+ totalSize += gap * (span - 1);
+
+ // If the spanned component is bigger than the the rows or columns
+ // it spans, we divide the extra space based on the weights of the
+ // spanned rows or columns
+
+ if (compSize > totalSize) {
+ int extra = compSize - totalSize;
+ int totalWeight = 0;
+ for (int i = 0; i < span; i++) {
+ totalWeight += weight[pos + i];
+ }
+ if (totalWeight == 0) totalWeight = span;
+
+ int remainder = extra;
+ for (int i = 0; i < span; i++) {
+ int portion = (extra * weight[pos + i]) / totalWeight;
+ sizes[pos + i] += portion;
+ remainder -= portion;
+ }
+
+ // Because of truncation, we may have a little left over which
+ // we give to the last row or column
+
+ if (remainder > 0) {
+ sizes[pos + span - 1] += remainder;
+ }
+ }
+}
+
+//**********************************************************************
+// Inner Classes
+//**********************************************************************
+
+//**********************************************************************
+//
+// Attributes
+//
+// This class converts a string attribute into a data structure.
+//
+//**********************************************************************
+
+// Attributes not needing assigment
+
+static String[] attr = {
+ "tn", "tne", "tnw",
+ "ts", "tse", "tsw",
+ "te", "tw", "tc",
+ "tfh", "tfv", "tf",
+ "n", "ne", "nw",
+ "s", "se", "sw",
+ "e", "w", "c",
+ "fh", "fv", "f",
+};
+
+// Attributes needing assigment
+
+static String[] assgn = {
+ "cols",
+ "rgap",
+ "cgap",
+ "titop",
+ "tibottom",
+ "tileft",
+ "tiright",
+ "itop",
+ "ibottom",
+ "ileft",
+ "iright",
+ "rweight",
+ "cweight",
+ "rspan",
+ "cspan",
+ "col",
+ "skip"
+};
+
+private class Attributes
+{
+
+// Constants used for fill and placement operations
+
+static final int CENTER = 0;
+static final int LEFT = 1;
+static final int RIGHT = 2;
+static final int TOP = 3;
+static final int BOTTOM = 4;
+static final int FILL = 5;
+
+// Constants for column placement
+
+static final int NEXT_COLUMN = -1;
+
+// The attributes in their original string form
+
+String attrString;
+
+// Table-only options
+
+int columns = 1;
+int tableHorizontal = FILL;
+int tableVertical = FILL;
+int rGap = 0;
+int cGap = 0;
+Insets tableInsets = new Insets(0, 0, 0, 0);
+
+// Table/cell options
+
+int horizontal = FILL;
+int vertical = FILL;
+Insets cellInsets = new Insets(0, 0, 0, 0);
+int rWeight = 0;
+int cWeight = 0;
+
+// Cell-only options
+
+int rSpan = 1;
+int cSpan = 1;
+int originalCSpan = cSpan;
+int column = NEXT_COLUMN;
+int skip = 0;
+
+int tkPos = 0;
+boolean isTableAttributes = false;
+
+Attributes(
+ String attrString)
+{
+ this(attrString, true);
+}
+
+Attributes(
+ String attrString,
+ boolean isTableAttributes)
+{
+ // Save the string for later access
+
+ this.attrString = attrString;
+
+ this.isTableAttributes = isTableAttributes;
+
+ parse();
+}
+
+public String
+toString()
+{
+ String sep = System.getProperty("line.separator");
+ return "TableLayout Attributes:" + sep +
+ "isTableAttributes = " + isTableAttributes + sep +
+ "columns = " + columns + sep +
+ "tableHorizontal = " + tableHorizontal + " " +
+ "tableVertical = " + tableVertical + sep +
+ "rGap = " + rGap + " " +
+ "cGap = " + cGap + sep +
+ "tableInsets = " + tableInsets + sep +
+ "horizontal = " + horizontal + " " +
+ "vertical = " + vertical + sep +
+ "cellInsets = " + cellInsets + sep +
+ "rWeight = " + rWeight + " " +
+ "cWeight = " + cWeight + sep +
+ "rSpan = " + rSpan + " " +
+ "cSpan = " + cSpan + sep +
+ "originalCSpan = " + originalCSpan + sep +
+ "column = " + column + sep +
+ "skip = " + skip;
+}
+
+String
+getStringAttributes()
+{
+ return attrString;
+}
+
+private char
+getTokenChar()
+{
+ if (tkPos >= attrString.length()) return 0;
+ return Character.toLowerCase(attrString.charAt(tkPos++));
+}
+
+
+private String
+getToken()
+{
+ StringBuffer token = new StringBuffer();
+
+ char c = getTokenChar();
+
+ // Skip whitespace
+
+ if (Character.isWhitespace(c)) {
+ do {
+ c = getTokenChar();
+ }
+ while (Character.isWhitespace(c));
+ }
+
+ // Attributes
+
+ if (Character.isLetter(c)) {
+ do {
+ token.append(c);
+ c = getTokenChar();
+ }
+ while (Character.isLetter(c));
+ if (c != 0) tkPos--;
+ }
+
+ // Integers
+
+ else if (Character.isDigit(c)) {
+ do {
+ token.append(c);
+ c = getTokenChar();
+ }
+ while (Character.isDigit(c));
+ if (c != 0) tkPos--;
+ }
+
+ // End of string
+
+ else if (c == 0) {
+ return null;
+ }
+
+ // Everything else is a single-character token
+
+ else {
+ token.append(c);
+ }
+
+ return new String(token);
+}
+
+void
+parse()
+{
+ // Initialize this set of attributes so it starts out as a copy of
+ // the given default, at least for those options where the cell
+ // can override a table default
+
+ if (!isTableAttributes) {
+ horizontal = tableAttributes.horizontal;
+ vertical = tableAttributes.vertical;
+ cellInsets = (Insets)tableAttributes.cellInsets.clone();
+ rWeight = tableAttributes.rWeight;
+ cWeight = tableAttributes.cWeight;
+ }
+
+ if (attrString == null) return;
+ tkPos = 0;
+
+ while (tkPos < attrString.length()) {
+ parseOption();
+ }
+
+ // We have checked the syntax, now check the semantics
+
+ if (isTableAttributes) {
+ if (columns == 0) {
+ reportSemanticError("cols=0");
+ }
+ }
+ else {
+ if (rSpan == 0) {
+ reportSemanticError("rspan=0");
+ }
+ if (cSpan == 0) {
+ reportSemanticError("cspan=0");
+ }
+ if (column >= tableAttributes.columns) {
+ reportSemanticError("col=" + column +
+ " (max is " +
+ (tableAttributes.columns - 1) + ")");
+ }
+ }
+}
+
+private void
+parseOption()
+{
+ // Get the next token
+
+ String token = getToken();
+ if (token == null) return;
+
+ boolean attributeFound = false;
+ for (int i = 0; i < attr.length; i++) {
+ if (token.equals(attr[i])) {
+ parseAttribute(token);
+ return;
+ }
+ }
+
+ for (int i = 0; i < assgn.length; i++) {
+ if (token.equals(assgn[i])) {
+ parseAssignment(token);
+ return;
+ }
+ }
+
+ reportError(token, "Unrecognized attribute");
+}
+
+private void
+parseAttribute(
+ String token)
+{
+ // Table placement and fill
+
+ if ("tnw".equals(token) ||
+ "tw".equals(token) ||
+ "tsw".equals(token)) {
+ tableHorizontal = LEFT;
+ }
+
+ if ("tne".equals(token) ||
+ "te".equals(token) ||
+ "tse".equals(token)) {
+ tableHorizontal = RIGHT;
+ }
+
+ if ("tn".equals(token) ||
+ "tc".equals(token) ||
+ "ts".equals(token)) {
+ tableHorizontal = CENTER;
+ }
+
+ if ("tf".equals(token) ||
+ "tfh".equals(token)) {
+ tableHorizontal = FILL;
+ }
+
+ if ("tn".equals(token) ||
+ "tnw".equals(token) ||
+ "tne".equals(token)) {
+ tableVertical = TOP;
+ }
+
+ if ("ts".equals(token) ||
+ "tsw".equals(token) ||
+ "tse".equals(token)) {
+ tableVertical = BOTTOM;
+ }
+
+ if ("tw".equals(token) ||
+ "tc".equals(token) ||
+ "te".equals(token)) {
+ tableVertical = CENTER;
+ }
+
+ if ("tf".equals(token) ||
+ "tfv".equals(token)) {
+ tableVertical = FILL;
+ }
+
+ // Cell placement and fill
+
+ if ("nw".equals(token) ||
+ "w".equals(token) ||
+ "sw".equals(token)) {
+ horizontal = LEFT;
+ }
+
+ if ("ne".equals(token) ||
+ "e".equals(token) ||
+ "se".equals(token)) {
+ horizontal = RIGHT;
+ }
+
+ if ("n".equals(token) ||
+ "c".equals(token) ||
+ "s".equals(token)) {
+ horizontal = CENTER;
+ }
+
+ if ("f".equals(token) ||
+ "fh".equals(token)) {
+ horizontal = FILL;
+ }
+
+ if ("n".equals(token) ||
+ "nw".equals(token) ||
+ "ne".equals(token)) {
+ vertical = TOP;
+ }
+
+ if ("s".equals(token) ||
+ "sw".equals(token) ||
+ "se".equals(token)) {
+ vertical = BOTTOM;
+ }
+
+ if ("w".equals(token) ||
+ "c".equals(token) ||
+ "e".equals(token)) {
+ vertical = CENTER;
+ }
+
+ if ("f".equals(token) ||
+ "fv".equals(token)) {
+ vertical = FILL;
+ }
+}
+
+private void
+parseAssignment(
+ String token)
+{
+ String attr = token;
+
+ token = getToken();
+ if (token != null) {
+ if ("=".equals(token)) {
+ token = getToken();
+ if (token != null) {
+ int value = 0;
+ try {
+ value = Integer.parseInt(token);
+ }
+ catch (NumberFormatException e) {
+ reportError(token, "Expected an integer");
+ }
+
+ if ("cols".equals(attr))
+ columns = value;
+ else if ("rgap".equals(attr))
+ rGap = value;
+ else if ("cgap".equals(attr))
+ cGap = value;
+ else if ("titop".equals(attr))
+ tableInsets.top = value;
+ else if ("tibottom".equals(attr))
+ tableInsets.bottom = value;
+ else if ("tileft".equals(attr))
+ tableInsets.left = value;
+ else if ("tiright".equals(attr))
+ tableInsets.right = value;
+ else if ("itop".equals(attr))
+ cellInsets.top = value;
+ else if ("ibottom".equals(attr))
+ cellInsets.bottom = value;
+ else if ("ileft".equals(attr))
+ cellInsets.left = value;
+ else if ("iright".equals(attr))
+ cellInsets.right = value;
+ else if ("rweight".equals(attr))
+ rWeight = value;
+ else if ("cweight".equals(attr))
+ cWeight = value;
+ else if ("rspan".equals(attr))
+ rSpan = value;
+ else if ("cspan".equals(attr))
+ originalCSpan = cSpan = value;
+ else if ("col".equals(attr))
+ column = value;
+ else if ("skip".equals(attr))
+ skip = value;
+
+ return;
+ }
+ }
+ reportError(token, "Expected an '='");
+ }
+ reportError(token, "Expected an '='");
+}
+
+private void
+reportError(
+ String token,
+ String message)
+{
+ throw new IllegalArgumentException(
+ "TableLayout: " + message + "; near '" + token +
+ "' at position " + tkPos + " in '" +
+ attrString + "'");
+}
+
+private void
+reportSemanticError(
+ String message)
+{
+ throw new IllegalArgumentException(
+ "TableLayout: Invalid value: " + message);
+}
+
+}
+
+//**********************************************************************
+//
+// CompArray
+//
+// We'd like to use a 2-dimensional array to help us sort out the
+// layout of the various components. But in order to create the array,
+// we need to know the number of rows. In order to get the number of
+// rows, we need to lay out the components. So we have problem.
+//
+// It is not obvious how to determine the number of rows from the
+// component count, due to row and column spans. So we've created the
+// CompArray class to help out. It makes its best guess at the size of
+// the array. If we need additional rows, it expands the array as
+// efficiently as it can. When we're done, we can ask it to create a
+// correctly sized array to hold our data.
+//
+//**********************************************************************
+
+private class CompArray
+{
+
+private int nCols;
+private int nRows;
+private int maxRow = 0;
+Component[][] compArray = null;
+
+CompArray(
+ int nCols,
+ int compCount)
+{
+ this.nRows = (compCount + (nCols - 1)) / nCols;
+ this.nRows = Math.max(this.nRows, 1);
+ this.nCols = nCols;
+
+ compArray = new Component[nRows][];
+ for (int i = 0; i < nRows; i++) {
+ compArray[i] = new Component[nCols];
+ Arrays.fill(compArray[i], null);
+ }
+}
+
+Component
+get(
+ int row,
+ int col)
+{
+ if (row >= nRows) resize(row + 1);
+ return compArray[row][col];
+}
+
+void
+set(
+ int row,
+ int col,
+ Component comp)
+{
+ if (row >= nRows) resize(row + 1);
+ compArray[row][col] = comp;
+ maxRow = Math.max(row, maxRow);
+}
+
+Component[][]
+getArray()
+{
+ int maxRows = maxRow + 1;
+ Component[][] array = new Component[maxRows][];
+ for (int r = 0; r < maxRows; r++) {
+ array[r] = new Component[nCols];
+ System.arraycopy(compArray[r], 0, array[r], 0, nCols);
+ }
+
+ return array;
+}
+
+private void
+resize(
+ int newRows)
+{
+ // When we exceed a threshold, bump up the size by at least 10
+
+ if (newRows - nRows < 10) newRows = nRows + 10;
+
+ // Create the new row array and copy the old one into it
+
+ Component[][] newArray = new Component[newRows][];
+ System.arraycopy(compArray, 0, newArray, 0, nRows);
+
+ // Initialize each new row to nulls
+
+ for (int i = nRows; i < newRows; i++) {
+ newArray[i] = new Component[nCols];
+ Arrays.fill(newArray[i], null);
+ }
+
+ compArray = newArray;
+ nRows = newRows;
+}
+
+}
+
+//**********************************************************************
+// End Inner Classes
+//**********************************************************************
+
+}