All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.clearthought.layout.TableLayout Maven / Gradle / Ivy

There is a newer version: 4.3.0
Show newest version
package info.clearthought.layout;

import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;


/**
 * TableLayout is a layout manager that is more powerful than GridBagLayout yet much easier to use.
 * 

* * Background * *

* TableLayout is a layout manager that arranges components in rows and columns like a spreadsheet. TableLayout allows * each row or column to be a different size. A row or column can be given an absolute size in pixels, a percentage of * the available space, or it can grow and shrink to fill the remaining space after other rows and columns have been * resized. *

* *

* Using spreadsheet terminology, a cell is the intersection of a row and column. Cells have finite, non-negative sizes * measured in pixels. The dimensions of a cell depend solely upon the dimensions of its row and column. *

* *

* A component occupies a rectangular group of one or more cells. The component can be aligned within those cells using * four vertical and six horizontal justifications. The vertical justifications are left, center, right, and full. The * horizontal justifications are left, center, right, full, leading, and trailing. With full justification the component * is stretched either vertically or horizontally to fit the cell or group of cells. *

* * Justification * *

* Leading and trailing justification are used to support languages that are read from right to left. See the * java.awt.ComponentOrientation class for details and http://java.sun.com/products/jfc/tsc/articles/bidi * for an introduction to component orientation and bidirectional text support. The leading justification will align the * component along the leading edge of the container and the trailing justification will align the component along the * trailing edge. There is no leading or trailing justification along the vertical axis since all modern languages are * read from top to bottom and no bottom-to-top orientation is defined in java.awt.ComponentOrientation. *

* *

* For components using the ComponentOrientation.LEFT_TO_RIGHT orientation, the leading edge is the left * edge and the trailing edge is the right one. For components using the ComponentOrientation.RIGHT_TO_LEFT * orientation, the opposite is true. For components that are using ComponentOrientation.UNKNOWN * and for Java runtime environments that do not support component orientation, left-to-right orientation is assumed for * backwards compatibility. *

* * Gaps * *

* Horizontal and vertical gaps can be placed between rows and columns in two ways. If uniformed gaps are desired, the * setHGap and * setVGap methods may be used. To vary the size of gaps, simply use empty rows and columns with absolute sizes. * Similarly, to make a border around a container that does not have insets, use empty rows and columns along the edges * of the container. *

* * Constraints * *

* Using TableLayout is a simple two step process. First, create a grid for your container by specifying row and column * sizes using either a TableLayout constructor or the insertRow and insertColumn methods. * Second, add components to the cells formed by the rows and columns. *

* *

* When adding a component to a container that uses TableLayout, you specify the component's constraints that state * which cells the component will occupy and how the component will be aligned. The constraints can be specified into * two ways. The TableLayoutConstraints class can be used to systematically specify the constraints. This * is useful to dynamic code, bean builders, and rapid application development software. *

* *

* For manual coding, a quicker and easier way to specify constraints is with a short string in the form * "x1, y1, x2, y2, hAlign, vAlign" where (x1, y1) identifies the top left cell (column x1, row y1) for the component * and (x2, y2) identifies the bottom right cell. x2 and y2 are optional. If they are not specified, the component will * occupy only one cell, (x1, y1). hAlign and vAlign are also optional with default values of full justification. * Alignments may be spelt fully as in "LEFT" or abbreviated as in "L". The text is not case sensitive, but it is * recommended that uppercase is used for two reasons. First, these text values are in essence constants. Second, some * fonts use the same glyphs for representing a lowercase L and the number one. Ex., "l" vs. "1". Even fonts that do not * will often use similar glyphs so using uppercase avoids confusion. *

* * Dynamically altering the layout * *

* Rows and columns can be dynamically created, resized, and removed at any time, even if the container is visible. * Components will be shifted appropriately as rows and columns are inserted or removed, just as cells are shifted in a * spreadsheet. *

* *

* Rows and columns can be made "hidden" or effectively invisible by setting their size to zero. They can be shown again * by setting their size back to a non-zero value. This is very useful for toggle form elements without having to remove * individual components. *

* * Preferred sizes * *

* Often it is desirable to make a row or column just large enough to ensure that all components contained partially or * wholly in that row or column are their preferred size. To make this easy, there is a constant called * PREFERRED that can be used to specify row or column sizes. There is another constant called * MINIMUM that does a similar task using components' minimum sizes instead of their preferred sizes. *

* *

* There is no corresponding MAXIMUM constant for several reasons. First, it is mathematically impossible * to honor both the minimum and maximum sizes of more than one component when conflicts arise. For example, say * components a and b are in the same row. If a's maximum height is less than b's minimum height, then one of these * constraints must be violated. Since TableLayout is a complete, general Cartesian layout manager, it would be possible * to specify conflicting constraints if a MAXIMUM * constant existed. *

* *

* Second, the ability to make a component grow up to a maximum size is primarily of interest to layout managers like * SpringLayout that have to balance the sizes of components because the presence of one component affects * the size of another. Other than the effect of preferred and minimum size rows/columns, which are essentially * convenient ways of specifying absolute sizes, the existence and constraints of one component does not affect any * other components when using TableLayout. This is accomplished because rows and columns are explicit in TableLayout. *

* *

* Third, the ability to constrain a component to its maximum size is subsumed by the ability to constrain it to its * preferred size, which is precisely what happens when a component is aligned using anything but full justification. In * the case of full justification, the component's maximum size is by definition unbounded. *

* * Example * *
 * import java.awt.*;
 * import javax.swing.*;
 * import info.clearthought.layout.TableLayout;
 * 
 * public class Preferred extends JFrame
 * {
 * 
 *     public static void main (String args[])
 *     {
 *         new Preferred();
 *     }
 * 
 *     public Preferred ()
 *     {
 *         super("The Power of Preferred Sizes");
 *         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 *         Container pane = getContentPane();
 * 
 *         // b - border
 *         // f - FILL
 *         // p - PREFERRED
 *         // vs - vertical space between labels and text fields
 *         // vg - vertical gap between form elements
 *         // hg - horizontal gap between form elements
 * 
 *         double b = 10;
 *         double f = TableLayout.FILL;
 *         double p = TableLayout.PREFERRED;
 *         double vs = 5;
 *         double vg = 10;
 *         double hg = 10;
 * 
 *         double size[][] =
 *             {{b, f, hg, p, hg, p, b},
 *              {b, p, vs, p, vg, p, vs, p, vg, p, vs, p, vg, p, b}};
 * 
 *         TableLayout layout = new TableLayout(size);
 *         pane.setLayout (layout);
 * 
 *         // Create all controls
 *         JLabel labelName    = new JLabel("Name");
 *         JLabel labelAddress = new JLabel("Address");
 *         JLabel labelCity    = new JLabel("City");
 *         JLabel labelState   = new JLabel("State");
 *         JLabel labelZip     = new JLabel("Zip");
 * 
 *         JTextField textfieldName    = new JTextField(10);
 *         JTextField textfieldAddress = new JTextField(20);
 *         JTextField textfieldCity    = new JTextField(10);
 *         JTextField textfieldState   = new JTextField(2);
 *         JTextField textfieldZip     = new JTextField(5);
 * 
 *         JButton buttonOk = new JButton("OK");
 *         JButton buttonCancel = new JButton("Cancel");
 *         JPanel panelButton = new JPanel();
 *         panelButton.add(buttonOk);
 *         panelButton.add(buttonCancel);
 * 
 *         // Add all controls
 *         pane.add(labelName,        "1,  1, 5, 1");
 *         pane.add(textfieldName,    "1,  3, 5, 3");
 *         pane.add(labelAddress,     "1,  5, 5, 5");
 *         pane.add(textfieldAddress, "1,  7, 5, 7");
 *         pane.add(labelCity,        "1,  9");
 *         pane.add(textfieldCity,    "1, 11");
 *         pane.add(labelState,       "3,  9");
 *         pane.add(textfieldState,   "3, 11");
 *         pane.add(labelZip,         "5,  9");
 *         pane.add(textfieldZip,     "5, 11");
 *         pane.add(panelButton,      "1, 13, 5, 13");
 * 
 *         pack();
 *         setResizable(false);
 *         show();
 *     }
 * }
 * 
* * @version 4.2 August 26, 2009 * @author Daniel E. Barbalace */ public class TableLayout implements LayoutManager2, Serializable, TableLayoutConstants { /* * Note: In this file, a cr refers to either a column or a row. cr[C] always means column and cr[R] always means row. * A cr size is either a column width or a row Height. TableLayout views columns and rows as being conceptually * symmetric. Therefore, much of the code applies to both columns and rows, and the use of the cr terminology * eliminates redundancy. Also, for ease of reading, z always indicates a parameter whose value is either C or R. */ private static final long serialVersionUID = -4330695726934191524L; /** Default row/column size */ protected static final double defaultSize[][] = { {}, {}}; /** Indicates a column */ protected static final int C = 0; /** Indicates a row */ protected static final int R = 1; /** Used to minimize reflection calls */ protected static boolean checkForComponentOrientationSupport = true; /** * Method used to get component orientation while preserving compatibility with earlier versions of * java.awt.Container. Necessary for supporting older JDKs and MicroEdition versions of Java. */ protected static Method methodGetComponentOrientation; /** Sizes of crs expressed in absolute and relative terms */ protected double crSpec[][] = {null, null}; /** Sizes of crs in pixels */ protected int crSize[][] = {null, null}; /** * Offsets of crs in pixels. The left boarder of column n is at crOffset[C][n] and the right boarder is at cr[C][n + * 1] for all columns including the last one. crOffset[C].length = crSize[C].length + 1 */ protected int crOffset[][] = {null, null}; /** List of components and their sizes */ protected LinkedList list; /** * Indicates whether or not the size of the cells are known for the last known size of the container. If dirty is true * or the container has been resized, the cell sizes must be recalculated using calculateSize. */ protected boolean dirty; /** Previous known width of the container */ protected int oldWidth; /** Previous known height of the container */ protected int oldHeight; /** Horizontal gap between columns */ protected int hGap; /** Vertical gap between rows */ protected int vGap; /** * Constructs an instance of TableLayout. This TableLayout will have no columns or rows. This constructor is most * useful for bean-oriented programming and dynamically adding columns and rows. */ public TableLayout() { init(defaultSize[C], defaultSize[R]); } /** * Constructs an instance of TableLayout. * * @param size widths of columns and heights of rows in the format, {{col0, col1, col2, ..., colN}, {row0, row1, row2, * ..., rowM}} If this parameter is invalid, the TableLayout will have exactly one row and one column. */ public TableLayout(double[][] size) { // Make sure columns and rows and nothing else is specified if ((size != null) && (size.length == 2)) init(size[C], size[R]); else throw new IllegalArgumentException("Parameter size should be an array, a[2], where a[0] is the " + "is an array of column widths and a[1] is an array or row " + "heights."); } /** * Constructs an instance of TableLayout. * * @param col widths of columns in the format, {{col0, col1, col2, ..., colN} * @param row heights of rows in the format, {{row0, row1, row2, ..., rowN} */ public TableLayout(double[] col, double[] row) { init(col, row); } /** * Initializes the TableLayout for all constructors. * * @param col widths of columns in the format, {{col0, col1, col2, ..., colN} * @param row heights of rows in the format, {{row0, row1, row2, ..., rowN} */ protected void init(double[] col, double[] row) { // Check parameters if (col == null) throw new IllegalArgumentException("Parameter col cannot be null"); if (row == null) throw new IllegalArgumentException("Parameter row cannot be null"); // Create new rows and columns crSpec[C] = new double[col.length]; crSpec[R] = new double[row.length]; // Copy rows and columns System.arraycopy(col, 0, crSpec[C], 0, crSpec[C].length); System.arraycopy(row, 0, crSpec[R], 0, crSpec[R].length); // Make sure rows and columns are valid for (int counter = 0; counter < crSpec[C].length; counter++) if ((crSpec[C][counter] < 0.0) && (crSpec[C][counter] != FILL) && (crSpec[C][counter] != PREFERRED) && (crSpec[C][counter] != MINIMUM)) { crSpec[C][counter] = 0.0; } for (int counter = 0; counter < crSpec[R].length; counter++) if ((crSpec[R][counter] < 0.0) && (crSpec[R][counter] != FILL) && (crSpec[R][counter] != PREFERRED) && (crSpec[R][counter] != MINIMUM)) { crSpec[R][counter] = 0.0; } // Create an empty list of components list = new LinkedList(); // Indicate that the cell sizes are not known dirty = true; } /** * Gets the constraints of a given component. * * @param component desired component * * @return If the given component is found, the constraints associated with that component. If the given component is * null or is not found, null is returned. */ public TableLayoutConstraints getConstraints(Component component) { ListIterator iterator = list.listIterator(0); while (iterator.hasNext()) { Entry entry = iterator.next(); if (entry.component == component) return new TableLayoutConstraints(entry.cr1[C], entry.cr1[R], entry.cr2[C], entry.cr2[R], entry.alignment[C], entry.alignment[R]); } return null; } /** * Sets the constraints of a given component. * * @param component desired component. This parameter cannot be null. * @param constraint new set of constraints. This parameter cannot be null. */ public void setConstraints(Component component, TableLayoutConstraints constraint) { // Check parameters if (component == null) throw new IllegalArgumentException("Parameter component cannot be null."); else if (constraint == null) throw new IllegalArgumentException("Parameter constraint cannot be null."); // Find and update constraints for the given component ListIterator iterator = list.listIterator(0); while (iterator.hasNext()) { Entry entry = iterator.next(); if (entry.component == component) iterator.set(new Entry(component, constraint)); } } /** * Adjusts the number and sizes of rows in this layout. After calling this method, the caller should request this * layout manager to perform the layout. This can be done with the following code: * *
   * layout.layoutContainer(container);
   * container.repaint();
   * 
* * or * *
   * window.pack()
   * 
* * If this is not done, the changes in the layout will not be seen until the container is resized. * * @param column widths of each of the columns * * @see #getColumn */ public void setColumn(double column[]) { setCr(C, column); } /** * Adjusts the number and sizes of rows in this layout. After calling this method, the caller should request this * layout manager to perform the layout. This can be done with the following code: * * * layout.layoutContainer(container); * container.repaint(); * * * or * *
   * window.pack()
   * 
* * If this is not done, the changes in the layout will not be seen until the container is resized. * * @param row heights of each of the rows. This parameter cannot be null. * * @see #getRow */ public void setRow(double row[]) { setCr(R, row); } /** * Sets the sizes of rows or columns for the methods setRow or setColumn. * * @param z indicates row or column * @param size new cr size */ protected void setCr(int z, double size[]) { // Copy crs crSpec[z] = new double[size.length]; System.arraycopy(size, 0, crSpec[z], 0, crSpec[z].length); // Make sure rows are valid for (int counter = 0; counter < crSpec[z].length; counter++) if ((crSpec[z][counter] < 0.0) && (crSpec[z][counter] != FILL) && (crSpec[z][counter] != PREFERRED) && (crSpec[z][counter] != MINIMUM)) { crSpec[z][counter] = 0.0; } // Indicate that the cell sizes are not known dirty = true; } /** * Adjusts the width of a single column in this layout. After calling this method, the caller should request this * layout manager to perform the layout. This can be done with the following code: * * * layout.layoutContainer(container); * container.repaint(); * * * or * *
   * window.pack()
   * 
* * If this is not done, the changes in the layout will not be seen until the container is resized. * * @param i zero-based index of column to set. If this parameter is not valid, an ArrayOutOfBoundsException will be * thrown. * @param size width of the column. This parameter cannot be null. * * @see #getColumn */ public void setColumn(int i, double size) { setCr(C, i, size); } /** * Adjusts the height of a single row in this layout. After calling this method, the caller should request this layout * manager to perform the layout. This can be done with the following code: * * * layout.layoutContainer(container); * container.repaint(); * * * or * *
   * window.pack()
   * 
* * If this is not done, the changes in the layout will not be seen until the container is resized. * * @param i zero-based index of row to set. If this parameter is not valid, an ArrayOutOfBoundsException will be * thrown. * @param size height of the row. This parameter cannot be null. * * @see #getRow */ public void setRow(int i, double size) { setCr(R, i, size); } /** * Sets the sizes of rows or columns for the methods setRow or setColumn. * * @param z indicates row or column * @param i indicates which cr to resize * @param size new cr size */ protected void setCr(int z, int i, double size) { // Make sure size is valid if ((size < 0.0) && (size != FILL) && (size != PREFERRED) && (size != MINIMUM)) { size = 0.0; } // Copy new size crSpec[z][i] = size; // Indicate that the cell sizes are not known dirty = true; } /** * Gets the sizes of columns in this layout. * * @return widths of each of the columns * * @see #setColumn */ public double[] getColumn() { // Copy columns double column[] = new double[crSpec[C].length]; System.arraycopy(crSpec[C], 0, column, 0, column.length); return column; } /** * Gets the height of a single row in this layout. * * @return height of the requested row * * @see #setRow */ public double[] getRow() { // Copy rows double row[] = new double[crSpec[R].length]; System.arraycopy(crSpec[R], 0, row, 0, row.length); return row; } /** * Gets the width of a single column in this layout. * * @param i zero-based index of row to get. If this parameter is not valid, an ArrayOutOfBoundsException will be * thrown. * * @return width of the requested column * * @see #setRow */ public double getColumn(int i) { return crSpec[C][i]; } /** * Gets the sizes of a row in this layout. * * @param i zero-based index of row to get. If this parameter is not valid, an ArrayOutOfBoundsException will be * thrown. * * @return height of each of the requested row * * @see #setRow */ public double getRow(int i) { return crSpec[R][i]; } /** * Gets the number of columns in this layout. * * @return the number of columns */ public int getNumColumn() { return crSpec[C].length; } /** * Gets the number of rows in this layout. * * @return the number of rows */ public int getNumRow() { return crSpec[R].length; } /** * Gets the horizontal gap between colunns. * * @return the horizontal gap in pixels */ public int getHGap() { return hGap; } /** * Gets the vertical gap between rows. * * @return the vertical gap in pixels */ public int getVGap() { return vGap; } /** * Sets the horizontal gap between colunns. * * @param hGap the horizontal gap in pixels */ public void setHGap(int hGap) { if (hGap >= 0) this.hGap = hGap; else throw new IllegalArgumentException("Parameter hGap must be non-negative."); } /** * Sets the vertical gap between rows. * * @param vGap the horizontal gap in pixels */ public void setVGap(int vGap) { if (vGap >= 0) this.vGap = vGap; else throw new IllegalArgumentException("Parameter vGap must be non-negative."); } /** * Inserts a column in this layout. All components to the right of the insertion point are moved right one column. The * container will need to be laid out after this method returns. See setColumn. * * @param i zero-based index at which to insert the column * @param size size of the column to be inserted * * @see #setColumn * @see #deleteColumn */ public void insertColumn(int i, double size) { insertCr(C, i, size); } /** * Inserts a row in this layout. All components below the insertion point are moved down one row. The container will * need to be laid out after this method returns. See setRow. * * @param i zero-based index at which to insert the row * @param size size of the row to be inserted * * @see #setRow * @see #deleteRow */ public void insertRow(int i, double size) { insertCr(R, i, size); } /** * Inserts a cr for the methods insertRow or insertColumn. * * @param z indicates row or column * @param i zero-based index at which to insert the cr * @param size size of cr being inserted */ protected void insertCr(int z, int i, double size) { // Make sure position is valid if ((i < 0) || (i > crSpec[z].length)) throw new IllegalArgumentException("Parameter i is invalid. i = " + i + ". Valid range is [0, " + crSpec[z].length + "]."); // Make sure row size is valid if ((size < 0.0) && (size != FILL) && (size != PREFERRED) && (size != MINIMUM)) { size = 0.0; } // Copy crs double cr[] = new double[crSpec[z].length + 1]; System.arraycopy(crSpec[z], 0, cr, 0, i); System.arraycopy(crSpec[z], i, cr, i + 1, crSpec[z].length - i); // Insert cr cr[i] = size; crSpec[z] = cr; // Move all components that are below the new cr ListIterator iterator = list.listIterator(0); while (iterator.hasNext()) { // Get next entry Entry entry = iterator.next(); // Is the first cr below the new cr if (entry.cr1[z] >= i) // Move first cr entry.cr1[z]++; // Is the second cr below the new cr if (entry.cr2[z] >= i) // Move second cr entry.cr2[z]++; } // Indicate that the cell sizes are not known dirty = true; } /** * Deletes a column in this layout. All components to the right of the deletion point are moved left one column. The * container will need to be laid out after this method returns. See setColumn. * * @param i zero-based index of column to delete * * @see #setColumn * @see #deleteColumn */ public void deleteColumn(int i) { deleteCr(C, i); } /** * Deletes a row in this layout. All components below the deletion point are moved up one row. The container will need * to be laid out after this method returns. See setRow. There must be at least two rows in order to * delete a row. * * @param i zero-based index of row to delete * * @see #setRow * @see #deleteRow */ public void deleteRow(int i) { deleteCr(R, i); } /** * Deletes a cr for the methods deleteRow or deleteColumn. * * @param z indicates row or column * @param i zero-based index of cr to delete */ protected void deleteCr(int z, int i) { // Make sure position is valid if ((i < 0) || (i >= crSpec[z].length)) throw new IllegalArgumentException("Parameter i is invalid. i = " + i + ". Valid range is [0, " + (crSpec[z].length - 1) + "]."); // Copy rows double cr[] = new double[crSpec[z].length - 1]; System.arraycopy(crSpec[z], 0, cr, 0, i); System.arraycopy(crSpec[z], i + 1, cr, i, crSpec[z].length - i - 1); // Delete row crSpec[z] = cr; // Move all components that are to below the row deleted ListIterator iterator = list.listIterator(0); while (iterator.hasNext()) { // Get next entry Entry entry = iterator.next(); // Is the first row below the new row if (entry.cr1[z] > i) // Move first row entry.cr1[z]--; // Is the second row below the new row if (entry.cr2[z] > i) // Move second row entry.cr2[z]--; } // Indicate that the cell sizes are not known dirty = true; } /** * Converts this TableLayout to a string. * * @return a string representing the columns and row sizes in the form * "{{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}" */ public String toString() { int counter; String value = "TableLayout {{"; if (crSpec[C].length > 0) { for (counter = 0; counter < crSpec[C].length - 1; counter++) value += crSpec[C][counter] + ", "; value += crSpec[C][crSpec[C].length - 1] + "}, {"; } else value += "}, {"; if (crSpec[R].length > 0) { for (counter = 0; counter < crSpec[R].length - 1; counter++) value += crSpec[R][counter] + ", "; value += crSpec[R][crSpec[R].length - 1] + "}}"; } else value += "}}"; return value; } /** * Determines whether or not there are any components with invalid constraints. An invalid constraint is one that * references a non-existing row or column. For example, on a table with five rows, row -1 and row 5 are both invalid. * Valid rows are 0 through 4, inclusively. This method is useful for debugging. * * @return a list of TableLayout.Entry instances referring to the invalid constraints and corresponding components * * @see #getOverlappingEntry */ public List getInvalidEntry() { LinkedList listInvalid = new LinkedList(); try { ListIterator iterator = list.listIterator(0); while (iterator.hasNext()) { Entry entry = (Entry) iterator.next(); if ((entry.cr1[R] < 0) || (entry.cr1[C] < 0) || (entry.cr2[R] >= crSpec[R].length) || (entry.cr2[C] >= crSpec[C].length)) { listInvalid.add((Entry)entry.copy()); } } } catch (CloneNotSupportedException error) { throw new RuntimeException("Unexpected CloneNotSupportedException"); } return listInvalid; } /** * Gets a list of overlapping components and their constraints. Two components overlap if they cover at least one * common cell. This method is useful for debugging. * * @return a list of zero or more TableLayout.Entry instances * * @see #getInvalidEntry */ public List getOverlappingEntry() { LinkedList listOverlapping = new LinkedList(); try { // Count constraints int numEntry = list.size(); // If there are no components, they can't be overlapping if (numEntry == 0) return listOverlapping; // Put entries in an array Entry entry[] = (Entry[]) list.toArray(new Entry[numEntry]); // Check all components for (int knowUnique = 1; knowUnique < numEntry; knowUnique++) for (int checking = knowUnique - 1; checking >= 0; checking--) if (((entry[checking].cr1[C] >= entry[knowUnique].cr1[C]) && (entry[checking].cr1[C] <= entry[knowUnique].cr2[C]) && (entry[checking].cr1[R] >= entry[knowUnique].cr1[R]) && (entry[checking].cr1[R] <= entry[knowUnique].cr2[R])) || ((entry[checking].cr2[C] >= entry[knowUnique].cr1[C]) && (entry[checking].cr2[C] <= entry[knowUnique].cr2[C]) && (entry[checking].cr2[R] >= entry[knowUnique].cr1[R]) && (entry[checking].cr2[R] <= entry[knowUnique].cr2[R]))) { listOverlapping.add((Entry)entry[checking].copy()); } } catch (CloneNotSupportedException error) { throw new RuntimeException("Unexpected CloneNotSupportedException"); } return listOverlapping; } /** * Calculates the sizes of the rows and columns based on the absolute and relative sizes specified in * crSpec[R] and crSpec[C] and the size of the container. The result is stored in * crSize[R] and crSize[C]. * * @param container container using this TableLayout */ protected void calculateSize(Container container) { // Get the container's insets Insets inset = container.getInsets(); // Get the size of the container's available space Dimension d = container.getSize(); int availableWidth = d.width - inset.left - inset.right; int availableHeight = d.height - inset.top - inset.bottom; // Compensate for horiztonal and vertical gaps if (crSpec[C].length > 0) availableWidth -= hGap * (crSpec[C].length - 1); if (crSpec[R].length > 0) availableHeight -= vGap * (crSpec[R].length - 1); // Create array to hold actual sizes in pixels crSize[C] = new int[crSpec[C].length]; crSize[R] = new int[crSpec[R].length]; // Assign absolute sizes (must be done before assignPrefMinSize) availableWidth = assignAbsoluteSize(C, availableWidth); availableHeight = assignAbsoluteSize(R, availableHeight); // Assign preferred and minimum sizes (must be done after assignAbsoluteSize) availableWidth = assignPrefMinSize(C, availableWidth, MINIMUM); availableWidth = assignPrefMinSize(C, availableWidth, PREFERRED); availableHeight = assignPrefMinSize(R, availableHeight, MINIMUM); availableHeight = assignPrefMinSize(R, availableHeight, PREFERRED); // Assign relative sizes availableWidth = assignRelativeSize(C, availableWidth); availableHeight = assignRelativeSize(R, availableHeight); // Assign fill sizes assignFillSize(C, availableWidth); assignFillSize(R, availableHeight); // Calculate cr offsets for efficiency calculateOffset(C, inset); calculateOffset(R, inset); // Indicate that the size of the cells are known for the container's // current size dirty = false; oldWidth = d.width; oldHeight = d.height; } /** * Assigns absolute sizes. * * @param z indicates row or column * @param availableSize amount of space available in the container * * @return the amount of space available after absolute crs have been assigned sizes */ protected int assignAbsoluteSize(int z, int availableSize) { int numCr = crSpec[z].length; for (int counter = 0; counter < numCr; counter++) if ((crSpec[z][counter] >= 1.0) || (crSpec[z][counter] == 0.0)) { crSize[z][counter] = (int) (crSpec[z][counter] + 0.5); availableSize -= crSize[z][counter]; } return availableSize; } /** * Assigns relative sizes. * * @param z indicates row or column * @param availableSize amount of space available in the container * * @return the amount of space available after relative crs have been assigned sizes */ protected int assignRelativeSize(int z, int availableSize) { int relativeSize = (availableSize < 0) ? 0 : availableSize; int numCr = crSpec[z].length; for (int counter = 0; counter < numCr; counter++) if ((crSpec[z][counter] > 0.0) && (crSpec[z][counter] < 1.0)) { crSize[z][counter] = (int) (crSpec[z][counter] * relativeSize + 0.5); availableSize -= crSize[z][counter]; } return availableSize; } /** * Assigns FILL sizes. * * @param z indicates row or column * @param availableSize amount of space available in the container */ protected void assignFillSize(int z, int availableSize) { // Skip if there is no more space to allocate if (availableSize <= 0) return; // Count the number of "fill" cells int numFillSize = 0; int numCr = crSpec[z].length; for (int counter = 0; counter < numCr; counter++) if (crSpec[z][counter] == FILL) numFillSize++; // If numFillSize is zero, the if statement below will always evaluate to // false and the division will not occur. // If there are more than one "fill" cell, slack may occur due to rounding // errors int slackSize = availableSize; // Assign "fill" cells equal amounts of the remaining space for (int counter = 0; counter < numCr; counter++) if (crSpec[z][counter] == FILL) { crSize[z][counter] = availableSize / numFillSize; slackSize -= crSize[z][counter]; } // Assign one pixel of slack to each FILL cr, starting at the last one, // until all slack has been consumed for (int counter = numCr - 1; (counter >= 0) && (slackSize > 0); counter--) { if (crSpec[z][counter] == FILL) { crSize[z][counter]++; slackSize--; } } } /** * Calculates the offset of each cr. * * @param z indicates row or column */ protected void calculateOffset(int z, Insets inset) { int numCr = crSpec[z].length; crOffset[z] = new int[numCr + 1]; crOffset[z][0] = (z == C) ? inset.left : inset.top; for (int counter = 0; counter < numCr; counter++) crOffset[z][counter + 1] = crOffset[z][counter] + crSize[z][counter]; } /** * Assigned sizes to preferred and minimum size columns and rows. This reduces the available width and height. Minimum * widths/heights must be calculated first because they affect preferred widths/heights, but not vice versa. The end * result is that any component contained wholly or partly in a column/row of minimum/preferred width or height will * get at least its minimum/preferred width or height, respectively. * * @param z indicates row or column * @param availableSize amount of space available in the container * @param typeOfSize indicates preferred or minimum * * @return the amount of space available after absolute crs have been assigned sizes */ protected int assignPrefMinSize(int z, int availableSize, double typeOfSize) { // Get variables referring to columns or rows (crs) int numCr = crSpec[z].length; // Address every cr for (int counter = 0; counter < numCr; counter++) // Is the current cr a preferred/minimum (based on typeOfSize) size if (crSpec[z][counter] == typeOfSize) { // Assume a maximum width of zero int maxSize = 0; // Find maximum preferred/min width of all components completely // or partially contained within this cr ListIterator iterator = list.listIterator(0); nextComponent: while (iterator.hasNext()) { Entry entry = iterator.next(); // Skip invalid entries if ((entry.cr1[z] < 0) || (entry.cr2[z] >= numCr)) continue nextComponent; // Find the maximum desired size of this cr based on all crs // the current component occupies if ((entry.cr1[z] <= counter) && (entry.cr2[z] >= counter)) { // Setup size and number of adjustable crs Dimension p = (typeOfSize == PREFERRED) ? entry.component.getPreferredSize() : entry.component.getMinimumSize(); int size = (p == null) ? 0 : ((z == C) ? p.width : p.height); int numAdjustable = 0; // Calculate for preferred size if (typeOfSize == PREFERRED) // Consider all crs this component occupies for (int entryCr = entry.cr1[z]; entryCr <= entry.cr2[z]; entryCr++) { // Subtract absolute, relative, and minumum cr // sizes, which have already been calculated if ((crSpec[z][entryCr] >= 0.0) || (crSpec[z][entryCr] == MINIMUM)) { size -= crSize[z][entryCr]; } // Count preferred/min width columns else if (crSpec[z][entryCr] == PREFERRED) numAdjustable++; // Skip any component that occupies a fill cr // because the fill should fulfill the size // requirements else if (crSpec[z][entryCr] == FILL) continue nextComponent; } // Calculate for minimum size else // Consider all crs this component occupies for (int entryCr = entry.cr1[z]; entryCr <= entry.cr2[z]; entryCr++) { // Subtract absolute and relative cr sizes, which // have already been calculated if (crSpec[z][entryCr] >= 0.0) size -= crSize[z][entryCr]; // Count preferred/min width columns else if ((crSpec[z][entryCr] == PREFERRED) || (crSpec[z][entryCr] == MINIMUM)) { numAdjustable++; } // Skip any component that occupies a fill cr // because the fill should fulfill the size // requirements else if (crSpec[z][entryCr] == FILL) continue nextComponent; } // Adjust remaining size by eliminating space for gaps between the crs this component occupies int numCrOccupiedByThisComponent = entry.cr2[z] - entry.cr1[z] + 1; if (numCrOccupiedByThisComponent > 1) { int gap = (z == 0) ? hGap : vGap; size -= (numCrOccupiedByThisComponent - 1) * gap; } // Divide the size evenly among the adjustable crs size = (int) Math.ceil(size / (double) numAdjustable); // Take the maximumn size if (maxSize < size) maxSize = size; } } // Assign preferred size crSize[z][counter] = maxSize; // Reduce available size availableSize -= maxSize; } return availableSize; } /** * To lay out the specified container using this layout. This method reshapes the components in the specified target * container in order to satisfy the constraints of all components. * *

* User code should not have to call this method directly. *

* * @param container container being served by this layout manager */ @Override public void layoutContainer(Container container) { // Calculate sizes if container has changed size or components were added Dimension d = container.getSize(); if (dirty || (d.width != oldWidth) || (d.height != oldHeight)) calculateSize(container); // Get component orientation and insets ComponentOrientation co = getComponentOrientation(container); boolean isRightToLeft = (co != null) && !co.isLeftToRight(); Insets insets = container.getInsets(); // Get components Component component[] = container.getComponents(); // Layout components for (int counter = 0; counter < component.length; counter++) { try { // Get the entry for the next component ListIterator iterator = list.listIterator(0); Entry entry = null; while (iterator.hasNext()) { entry = iterator.next(); if (entry.component == component[counter]) break; else entry = null; } // Skip any components that have not been place in a specific cell, // setting the skip component's bounds to zero if (entry == null) { component[counter].setBounds(0, 0, 0, 0); continue; } // The following block of code has been optimized so that the // preferred size of the component is only obtained if it is // needed. There are components in which the getPreferredSize // method is extremely expensive, such as data driven controls // with a large amount of data. // Get the preferred size of the component int preferredWidth = 0; int preferredHeight = 0; if ((entry.alignment[C] != FULL) || (entry.alignment[R] != FULL)) { Dimension preferredSize = component[counter].getPreferredSize(); preferredWidth = preferredSize.width; preferredHeight = preferredSize.height; } // Calculate the coordinates and size of the component int value[] = calculateSizeAndOffset(entry, preferredWidth, true); int x = value[0]; int w = value[1]; value = calculateSizeAndOffset(entry, preferredHeight, false); int y = value[0]; int h = value[1]; // Compensate for component orientation. if (isRightToLeft) x = d.width - x - w + insets.left - insets.right; // Move and resize component component[counter].setBounds(x, y, w, h); } catch (Exception error) { // If any error occurs, set the bounds of this component to zero // and continue component[counter].setBounds(0, 0, 0, 0); continue; } } } /** * Gets the container's component orientation. If a JDK that does not support component orientation is being used, * then null is returned. * * @param container Container whose orientation is being queried * * @return the container's orientation or null if no orientation is supported */ protected ComponentOrientation getComponentOrientation(Container container) { // This method is implemented to only get the class and method objects // once so as to reduce expensive reflection operations. If the reflection // fails, then component orientation is not supported. ComponentOrientation co = null; try { if (checkForComponentOrientationSupport) { methodGetComponentOrientation = Class.forName("java.awt.Container").getMethod("getComponentOrientation", new Class[0]); checkForComponentOrientationSupport = false; } if (methodGetComponentOrientation != null) { co = (ComponentOrientation) methodGetComponentOrientation.invoke(container, new Object[0]); } } catch (Exception e) { } return co; } /** * Calculates the vertical/horizontal offset and size of a component. * * @param entry entry containing component and contraints * @param preferredSize previously calculated preferred width/height of component * @param isColumn if true, this method is being called to calculate the offset/size of a column. if false,... of a * row. * * @return an array, a, of two integers such that a[0] is the offset and a[1] is the size */ protected int[] calculateSizeAndOffset(Entry entry, int preferredSize, boolean isColumn) { // Get references to cr properties int crOffset[] = isColumn ? this.crOffset[C] : this.crOffset[R]; int entryAlignment = isColumn ? entry.alignment[C] : entry.alignment[R]; // Determine cell set size int cellSetSize = isColumn ? crOffset[entry.cr2[C] + 1] - crOffset[entry.cr1[C]] : crOffset[entry.cr2[R] + 1] - crOffset[entry.cr1[R]]; // Determine the size of the component int size; if ((entryAlignment == FULL) || (cellSetSize < preferredSize)) size = cellSetSize; else size = preferredSize; // Since the component orientation is adjusted for in the layoutContainer // method, we can treat leading justification as left justification and // trailing justification as right justification. if (isColumn && (entryAlignment == LEADING)) entryAlignment = LEFT; if (isColumn && (entryAlignment == TRAILING)) entryAlignment = RIGHT; // Determine offset int offset; switch (entryAlignment) { case LEFT: // Align left/top side along left edge of cell offset = crOffset[isColumn ? entry.cr1[C] : entry.cr1[R]]; break; case RIGHT: // Align right/bottom side along right edge of cell offset = crOffset[(isColumn ? entry.cr2[C] : entry.cr2[R]) + 1] - size; break; case CENTER: // Center justify component offset = crOffset[isColumn ? entry.cr1[C] : entry.cr1[R]] + ((cellSetSize - size) >> 1); break; case FULL: // Align left/top side along left/top edge of cell offset = crOffset[isColumn ? entry.cr1[C] : entry.cr1[R]]; break; default: // This is a never should happen case, but just in case offset = 0; } // Compensate for gaps if (isColumn) { offset += hGap * entry.cr1[C]; int cumlativeGap = hGap * (entry.cr2[C] - entry.cr1[C]); switch (entryAlignment) { case RIGHT: offset += cumlativeGap; break; case CENTER: offset += cumlativeGap >> 1; break; case FULL: size += cumlativeGap; break; } } else { offset += vGap * entry.cr1[R]; int cumlativeGap = vGap * (entry.cr2[R] - entry.cr1[R]); switch (entryAlignment) { case BOTTOM: offset += cumlativeGap; break; case CENTER: offset += cumlativeGap >> 1; break; case FULL: size += cumlativeGap; break; } } // Package return values int value[] = {offset, size}; return value; } /** * Determines the preferred size of the container argument using this layout. The preferred size is the smallest size * that, if used for the container's size, will ensure that all components are at least as large as their preferred * size. This method cannot guarantee that all components will be their preferred size. For example, if component A * and component B are each allocate half of the container's width and component A wants to be 10 pixels wide while * component B wants to be 100 pixels wide, they cannot both be accommodated. Since in general components rather be * larger than their preferred size instead of smaller, component B's request will be fulfilled. The preferred size of * the container would be 200 pixels. * * @param container container being served by this layout manager * * @return a dimension indicating the container's preferred size */ public Dimension preferredLayoutSize(Container container) { return calculateLayoutSize(container, PREFERRED); } /** * Determines the minimum size of the container argument using this layout. The minimum size is the smallest size * that, if used for the container's size, will ensure that all components are at least as large as their minimum * size. This method cannot guarantee that all components will be their minimum size. For example, if component A and * component B are each allocate half of the container's width and component A wants to be 10 pixels wide while * component B wants to be 100 pixels wide, they cannot both be accommodated. Since in general components rather be * larger than their minimum size instead of smaller, component B's request will be fulfilled. The minimum size of the * container would be 200 pixels. * * @param container container being served by this layout manager * * @return a dimension indicating the container's minimum size */ public Dimension minimumLayoutSize(Container container) { return calculateLayoutSize(container, MINIMUM); } /** * Calculates the preferred or minimum size for the methods preferredLayoutSize and minimumLayoutSize. * * @param container container whose size is being calculated * @param typeOfSize indicates preferred or minimum * * @return a dimension indicating the container's preferred or minimum size */ protected Dimension calculateLayoutSize(Container container, double typeOfSize) { // Get preferred/minimum sizes Entry entryList[] = (Entry[]) list.toArray(new Entry[list.size()]); int numEntry = entryList.length; Dimension prefMinSize[] = new Dimension[numEntry]; for (int i = 0; i < numEntry; i++) prefMinSize[i] = (typeOfSize == PREFERRED) ? entryList[i].component.getPreferredSize() : entryList[i].component .getMinimumSize(); // Calculate sizes int width = calculateLayoutSize(container, C, typeOfSize, entryList, prefMinSize); int height = calculateLayoutSize(container, R, typeOfSize, entryList, prefMinSize); // Compensate for container's insets Insets inset = container.getInsets(); width += inset.left + inset.right; height += inset.top + inset.bottom; return new Dimension(width, height); } /** * Calculates the preferred or minimum size for the method calculateLayoutSize(Container container, double * typeOfSize). This method is passed the preferred/minimum sizes of the components so that the potentially expensive * methods getPreferredSize()/getMinimumSize() are not called twice for the same component. * * @param container container whose size is being calculated * @param z indicates row or column * @param typeOfSize indicates preferred or minimum * @param entryList list of Entry objects * @param prefMinSize list of preferred or minimum sizes * * @return a dimension indicating the container's preferred or minimum size */ protected int calculateLayoutSize(Container container, int z, double typeOfSize, Entry entryList[], Dimension prefMinSize[]) { Dimension size; // Preferred/minimum size of current component int scaledSize = 0; // Preferred/minimum size of scaled components int temp; // Temporary variable used to compare sizes int counter; // Counting variable // Get number of crs int numCr = crSpec[z].length; // Determine percentage of space allocated to fill components. This is // one minus the sum of all scalable components. double fillSizeRatio = 1.0; int numFillSize = 0; for (counter = 0; counter < numCr; counter++) if ((crSpec[z][counter] > 0.0) && (crSpec[z][counter] < 1.0)) fillSizeRatio -= crSpec[z][counter]; else if (crSpec[z][counter] == FILL) numFillSize++; // Adjust fill ratios to reflect number of fill rows/columns if (numFillSize > 1) fillSizeRatio /= numFillSize; // Cap fill ratio bottoms to 0.0 if (fillSizeRatio < 0.0) fillSizeRatio = 0.0; // Create array to hold actual sizes in pixels crSize[z] = new int[numCr]; // Assign absolute sizes (must be done before assignPrefMinSize) // This is done to calculate absolute cr sizes assignAbsoluteSize(z, 0); // Assign preferred and minimum sizes (must be done after assignAbsoluteSize) // This is done to calculate preferred/minimum cr sizes assignPrefMinSize(z, 0, MINIMUM); assignPrefMinSize(z, 0, PREFERRED); int crPrefMin[] = new int[numCr]; for (counter = 0; counter < numCr; counter++) if ((crSpec[z][counter] == PREFERRED) || (crSpec[z][counter] == MINIMUM)) { crPrefMin[counter] = crSize[z][counter]; } // Find maximum preferred/minimum size of all scaled components int numColumn = crSpec[C].length; int numRow = crSpec[R].length; int numEntry = entryList.length; for (int entryCounter = 0; entryCounter < numEntry; entryCounter++) { // Get next entry Entry entry = entryList[entryCounter]; // Make sure entry is in valid rows and columns if ((entry.cr1[C] < 0) || (entry.cr1[C] >= numColumn) || (entry.cr2[C] >= numColumn) || (entry.cr1[R] < 0) || (entry.cr1[R] >= numRow) || (entry.cr2[R] >= numRow)) { // Skip the bad component continue; } // Get preferred/minimum size of current component size = prefMinSize[entryCounter]; // ---------------------------------------------------------------------- // Calculate portion of component that is not absolutely sized int scalableSize = (z == C) ? size.width : size.height; for (counter = entry.cr1[z]; counter <= entry.cr2[z]; counter++) if (crSpec[z][counter] >= 1.0) scalableSize -= crSpec[z][counter]; else if ((crSpec[z][counter] == PREFERRED) || (crSpec[z][counter] == MINIMUM)) { scalableSize -= crPrefMin[counter]; } // ---------------------------------------------------------------------- // Determine total percentage of scalable space that the component // occupies by adding the relative columns and the fill columns double relativeSize = 0.0; for (counter = entry.cr1[z]; counter <= entry.cr2[z]; counter++) { // Cr is scaled if ((crSpec[z][counter] > 0.0) && (crSpec[z][counter] < 1.0)) // Add scaled size to relativeWidth relativeSize += crSpec[z][counter]; // Cr is fill else if ((crSpec[z][counter] == FILL) && (fillSizeRatio != 0.0)) // Add fill size to relativeWidth relativeSize += fillSizeRatio; } // Determine the total scaled size as estimated by this component if (relativeSize == 0) temp = 0; else temp = (int) (scalableSize / relativeSize + 0.5); // ---------------------------------------------------------------------- // If the container needs to be bigger, make it so if (scaledSize < temp) scaledSize = temp; } // totalSize is the scaledSize plus the sum of all absolute sizes and all // preferred sizes int totalSize = scaledSize; for (counter = 0; counter < numCr; counter++) // Is the current cr an absolute size if (crSpec[z][counter] >= 1.0) totalSize += (int) (crSpec[z][counter] + 0.5); // Is the current cr a preferred/minimum size else if ((crSpec[z][counter] == PREFERRED) || (crSpec[z][counter] == MINIMUM)) { // Add preferred/minimum width totalSize += crPrefMin[counter]; } // Compensate for horizontal and vertical gap if (numCr > 0) totalSize += ((z == C) ? hGap : vGap) * (numCr - 1); return totalSize; } /** * Adds the specified component with the specified name to the layout. * * @param name indicates entry's position and anchor * @param component component to add */ @Override public void addLayoutComponent(String name, Component component) { addLayoutComponent(component, name); } /** * Adds the specified component with the specified name to the layout. * * @param component component to add * @param constraint indicates entry's position and alignment */ @Override public void addLayoutComponent(Component component, Object constraint) { if (constraint instanceof String) { // Create an entry to associate component with its constraints constraint = new TableLayoutConstraints((String) constraint); // Add component and constraints to the list list.add(new Entry(component, (TableLayoutConstraints) constraint)); // Indicate that the cell sizes are not known dirty = true; } else if (constraint instanceof TableLayoutConstraints) { // Add component and constraints to the list list.add(new Entry(component, (TableLayoutConstraints) constraint)); // Indicate that the cell sizes are not known dirty = true; } else if (constraint == null) throw new IllegalArgumentException("No constraint for the component"); else throw new IllegalArgumentException("Cannot accept a constraint of class " + constraint.getClass()); } /** * Removes the specified component from the layout. * * @param component component being removed */ @Override public void removeLayoutComponent(Component component) { // Remove the component ListIterator iterator = list.listIterator(0); while (iterator.hasNext()) { Entry entry = iterator.next(); if (entry.component == component) iterator.remove(); } // Indicate that the cell sizes are not known since dirty = true; } /** * Returns the maximum dimensions for this layout given the components in the specified target container. * * @param target the component which needs to be laid out * * @return unconditionally, a Dimension of Integer.MAX_VALUE by Integer.MAX_VALUE since TableLayout does not limit the * maximum size of a container */ @Override public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** * Returns the alignment along the x axis. This specifies how the component would like to be aligned relative to other * components. The value should be a number between 0 and 1 where 0 represents alignment along the origin, 1 is * aligned the furthest away from the origin, 0.5 is centered, etc. * * @return unconditionally, 0.5 */ @Override public float getLayoutAlignmentX(Container parent) { return 0.5f; } /** * Returns the alignment along the y axis. This specifies how the component would like to be aligned relative to other * components. The value should be a number between 0 and 1 where 0 represents alignment along the origin, 1 is * aligned the furthest away from the origin, 0.5 is centered, etc. * * @return unconditionally, 0.5 */ @Override public float getLayoutAlignmentY(Container parent) { return 0.5f; } /** * Invalidates the layout, indicating that if the layout manager has cached information it should be discarded. */ @Override public void invalidateLayout(Container target) { dirty = true; } /** * The following inner class is used to bind components to their constraints */ public static class Entry implements Cloneable { /** Component bound by the constraints */ public Component component; /** Cell in which the upper-left corner of the component lies */ public int cr1[]; /** Cell in which the lower-right corner of the component lies */ public int cr2[]; /** Horizontal and vertical alignment */ public int alignment[]; /** * Constructs an Entry that binds a component to a set of constraints. * * @param component component being bound * @param constraint constraints being applied */ public Entry(Component component, TableLayoutConstraints constraint) { int cr1[] = {constraint.col1, constraint.row1}; int cr2[] = {constraint.col2, constraint.row2}; int alignment[] = {constraint.hAlign, constraint.vAlign}; this.cr1 = cr1; this.cr2 = cr2; this.alignment = alignment; this.component = component; } /** * Copies this Entry. */ public Object copy() throws CloneNotSupportedException { return clone(); } /** * Gets the string representation of this Entry. * * @return a string in the form "(col1, row1, col2, row2, vAlign, hAlign) component" */ public String toString() { TableLayoutConstraints c = new TableLayoutConstraints(cr1[C], cr1[R], cr2[C], cr2[R], alignment[C], alignment[R]); return "(" + c + ") " + component; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy