0001: package org.rapla.components.layout;
0002:
0003: import java.awt.Color;
0004: import java.awt.Component;
0005: import java.awt.Container;
0006: import java.awt.Dimension;
0007: import java.awt.Graphics;
0008: import java.awt.Insets;
0009: import java.util.LinkedList;
0010: import java.util.ListIterator;
0011:
0012: /**
0013: * TableLayout is a layout manager that arranges components in rows and columns
0014: * like a spreadsheet. TableLayout allows each row or column to be a different
0015: * size. A row or column can be given an absolute size in pixels, a percentage
0016: * of the available space, or it can grow and shrink to fill the remaining space
0017: * after other rows and columns have been resized.
0018: *
0019: * <p>Using spreadsheet terminology, a cell is the intersection of a row and
0020: * column. Cells have finite, non-negative sizes measured in pixels. The
0021: * dimensions of a cell depend solely upon the dimensions of its row and column.
0022: * </p>
0023: *
0024: * <p>A component occupies a rectangular group of one or more cells. If the
0025: * component occupies more than one cell, the component is resized to fit
0026: * perfectly in the rectangular region of cells. If the component occupies a
0027: * single cell, it can be aligned in four ways within that cell.</p>
0028: *
0029: * <p>A single cell component can be stretched horizontally to fit the cell
0030: * (full justification), or it can be placed in the center of the cell. The
0031: * component could also be left justified or right justified. Similarly, the
0032: * component can be full, center, top, or bottom justified in the vertical.</p>
0033: *
0034: * <pre>
0035: * public static void main (String args[])
0036: * {
0037: * // Create a frame
0038: * Frame frame = new Frame("Example of TableLayout");
0039: * frame.setBounds (100, 100, 300, 300);
0040: * <spc>
0041: * // Create a TableLayout for the frame
0042: * double border = 10;
0043: * double size[][] =
0044: * {{border, 0.10, 20, TableLayout.FILL, 20, 0.20, border}, // Columns
0045: * {border, 0.20, 20, TableLayout.FILL, 20, 0.20, border}}; // Rows
0046: * <spc>
0047: * frame.setLayout (new TableLayout(size));
0048: * <spc>
0049: * // Create some buttons
0050: * String label[] = {"Top", "Bottom", "Left", "Right", "Center", "Overlap"};
0051: * Button button[] = new Button[label.length];
0052: * <spc>
0053: * for (int i = 0; i < label.length; i++)
0054: * button[i] = new Button(label[i]);
0055: * <spc>
0056: * // Add buttons
0057: * frame.add (button[0], "1, 1, 5, 1"); // Top
0058: * frame.add (button[1], "1, 5, 5, 5"); // Bottom
0059: * frame.add (button[2], "1, 3 "); // Left
0060: * frame.add (button[3], "5, 3 "); // Right
0061: * frame.add (button[4], "3, 3, c, c"); // Center
0062: * frame.add (button[5], "3, 3, 3, 5"); // Overlap
0063: * <spc>
0064: * // Allow user to close the window to terminate the program
0065: * frame.addWindowListener
0066: * (new WindowListener()
0067: * {
0068: * public void windowClosing (WindowEvent e)
0069: * {
0070: * System.exit (0);
0071: * }
0072: * <spc>
0073: * public void windowOpened (WindowEvent e) {}
0074: * public void windowClosed (WindowEvent e) {}
0075: * public void windowIconified (WindowEvent e) {}
0076: * public void windowDeiconified (WindowEvent e) {}
0077: * public void windowActivated (WindowEvent e) {}
0078: * public void windowDeactivated (WindowEvent e) {}
0079: * }
0080: * );
0081: * <spc>
0082: * // Show frame
0083: * frame.show();
0084: * }
0085: * </pre>
0086: *
0087: * @author Daniel E. Barbalace
0088: */
0089:
0090: public class TableLayout implements java.awt.LayoutManager2,
0091: TableLayoutConstants {
0092:
0093: /** Default row/column size */
0094: protected static final double defaultSize[][] = { {}, {} };
0095:
0096: /** Widths of columns expressed in absolute and relative terms */
0097: protected double columnSpec[];
0098:
0099: /** Heights of rows expressed in absolute and relative terms */
0100: protected double rowSpec[];
0101:
0102: /** Widths of columns in pixels */
0103: protected int columnSize[];
0104:
0105: /** Heights of rows in pixels */
0106: protected int rowSize[];
0107:
0108: /** Offsets of columns in pixels. The left boarder of column n is at
0109: columnOffset[n] and the right boarder is at columnOffset[n + 1] for all
0110: columns including the last one. columnOffset.length = columnSize.length + 1 */
0111: protected int columnOffset[];
0112:
0113: /** Offsets of rows in pixels. The left boarder of row n is at
0114: rowOffset[n] and the right boarder is at rowOffset[n + 1] for all
0115: rows including the last one. rowOffset.length = rowSize.length + 1 */
0116: protected int rowOffset[];
0117:
0118: /** List of components and their sizes */
0119: protected LinkedList list;
0120:
0121: /** Indicates whether or not the size of the cells are known for the last known
0122: size of the container. If dirty is true or the container has been resized,
0123: the cell sizes must be recalculated using calculateSize. */
0124: protected boolean dirty;
0125:
0126: /** Previous known width of the container */
0127: protected int oldWidth;
0128:
0129: /** Previous known height of the container */
0130: protected int oldHeight;
0131:
0132: //******************************************************************************
0133: //** Constructors ***
0134: //******************************************************************************
0135:
0136: /**
0137: * Constructs an instance of TableLayout. This TableLayout will have one row
0138: * and one column.
0139: */
0140:
0141: public TableLayout() {
0142: this (defaultSize);
0143: }
0144:
0145: /**
0146: * Constructs an instance of TableLayout.
0147: *
0148: * @param size widths of columns and heights of rows in the format,
0149: * {{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}
0150: * If this parameter is invalid, the TableLayout will have
0151: * exactly one row and one column.
0152: */
0153:
0154: public TableLayout(double size[][]) {
0155: // Make sure rows and columns and nothing else is specified
0156: if ((size != null) && (size.length == 2)) {
0157: // Get the rows and columns
0158: double tempCol[] = size[0];
0159: double tempRow[] = size[1];
0160:
0161: // Create new rows and columns
0162: columnSpec = new double[tempCol.length];
0163: rowSpec = new double[tempRow.length];
0164:
0165: // Copy rows and columns
0166: System.arraycopy(tempCol, 0, columnSpec, 0,
0167: columnSpec.length);
0168: System.arraycopy(tempRow, 0, rowSpec, 0, rowSpec.length);
0169:
0170: // Make sure rows and columns are valid
0171: for (int counter = 0; counter < columnSpec.length; counter++)
0172: if ((columnSpec[counter] < 0.0)
0173: && (columnSpec[counter] != FILL)
0174: && (columnSpec[counter] != PREFERRED)
0175: && (columnSpec[counter] != MINIMUM)) {
0176: columnSpec[counter] = 0.0;
0177: }
0178:
0179: for (int counter = 0; counter < rowSpec.length; counter++)
0180: if ((rowSpec[counter] < 0.0)
0181: && (rowSpec[counter] != FILL)
0182: && (rowSpec[counter] != PREFERRED)
0183: && (rowSpec[counter] != MINIMUM)) {
0184: rowSpec[counter] = 0.0;
0185: }
0186: } else {
0187: double tempCol[] = { FILL };
0188: double tempRow[] = { FILL };
0189:
0190: setColumn(tempCol);
0191: setRow(tempRow);
0192: }
0193:
0194: // Create an empty list of components
0195: list = new LinkedList();
0196:
0197: // Indicate that the cell sizes are not known
0198: dirty = true;
0199: }
0200:
0201: //******************************************************************************
0202: //** Get/Set methods ***
0203: //******************************************************************************
0204:
0205: /**
0206: * Gets the constraints of a given component.
0207: *
0208: * @param component desired component
0209: *
0210: * @return If the given component is found, the constraints associated with
0211: * that component. If the given component is null or is not found,
0212: * null is returned.
0213: */
0214:
0215: public TableLayoutConstraints getConstraints(Component component) {
0216: ListIterator iterator = list.listIterator(0);
0217:
0218: while (iterator.hasNext()) {
0219: Entry entry = (Entry) iterator.next();
0220:
0221: if (entry.component == component)
0222: return new TableLayoutConstraints(entry.col1,
0223: entry.row1, entry.col2, entry.row2,
0224: entry.hAlign, entry.vAlign);
0225: }
0226:
0227: return null;
0228: }
0229:
0230: /**
0231: * Sets the constraints of a given component.
0232: *
0233: * @param component desired component. This parameter cannot be null.
0234: * @param constraint new set of constraints. This parameter cannot be null.
0235: *
0236: */
0237:
0238: public void setConstraints(Component component,
0239: TableLayoutConstraints constraint) {
0240: // Check parameters
0241: if (component == null)
0242: throw new IllegalArgumentException(
0243: "Parameter component cannot be null.");
0244: else if (constraint == null)
0245: throw new IllegalArgumentException(
0246: "Parameter constraint cannot be null.");
0247:
0248: // Find and update constraints for the given component
0249: ListIterator iterator = list.listIterator(0);
0250:
0251: while (iterator.hasNext()) {
0252: Entry entry = (Entry) iterator.next();
0253:
0254: if (entry.component == component)
0255: iterator.set(new Entry(component, constraint));
0256: }
0257: }
0258:
0259: /**
0260: * Adjusts the number and sizes of rows in this layout. After calling this
0261: * method, the caller should request this layout manager to perform the
0262: * layout. This can be done with the following code:
0263: *
0264: * <pre>
0265: * layout.layoutContainer(container);
0266: * container.repaint();
0267: * </pre>
0268: *
0269: * or
0270: *
0271: * <pre>
0272: * window.pack()
0273: * </pre>
0274: *
0275: * If this is not done, the changes in the layout will not be seen until the
0276: * container is resized.
0277: *
0278: * @param column heights of each of the columns
0279: *
0280: * @see #getColumn
0281: */
0282:
0283: public void setColumn(double column[]) {
0284: // Copy columns
0285: columnSpec = new double[column.length];
0286: System.arraycopy(column, 0, columnSpec, 0, columnSpec.length);
0287:
0288: // Make sure columns are valid
0289: for (int counter = 0; counter < columnSpec.length; counter++)
0290: if ((columnSpec[counter] < 0.0)
0291: && (columnSpec[counter] != FILL)
0292: && (columnSpec[counter] != PREFERRED)
0293: && (columnSpec[counter] != MINIMUM)) {
0294: columnSpec[counter] = 0.0;
0295: }
0296:
0297: // Indicate that the cell sizes are not known
0298: dirty = true;
0299: }
0300:
0301: /**
0302: * Adjusts the number and sizes of rows in this layout. After calling this
0303: * method, the caller should request this layout manager to perform the
0304: * layout. This can be done with the following code:
0305: *
0306: * <code>
0307: * layout.layoutContainer(container);
0308: * container.repaint();
0309: * </code>
0310: *
0311: * or
0312: *
0313: * <pre>
0314: * window.pack()
0315: * </pre>
0316: *
0317: * If this is not done, the changes in the layout will not be seen until the
0318: * container is resized.
0319: *
0320: * @param row widths of each of the rows. This parameter cannot be null.
0321: *
0322: * @see #getRow
0323: */
0324:
0325: public void setRow(double row[]) {
0326: // Copy rows
0327: rowSpec = new double[row.length];
0328: System.arraycopy(row, 0, rowSpec, 0, rowSpec.length);
0329:
0330: // Make sure rows are valid
0331: for (int counter = 0; counter < rowSpec.length; counter++)
0332: if ((rowSpec[counter] < 0.0) && (rowSpec[counter] != FILL)
0333: && (rowSpec[counter] != PREFERRED)
0334: && (rowSpec[counter] != MINIMUM)) {
0335: rowSpec[counter] = 0.0;
0336: }
0337:
0338: // Indicate that the cell sizes are not known
0339: dirty = true;
0340: }
0341:
0342: /**
0343: * Adjusts the width of a single column in this layout. After calling this
0344: * method, the caller should request this layout manager to perform the
0345: * layout. This can be done with the following code:
0346: *
0347: * <code>
0348: * layout.layoutContainer(container);
0349: * container.repaint();
0350: * </code>
0351: *
0352: * or
0353: *
0354: * <pre>
0355: * window.pack()
0356: * </pre>
0357: *
0358: * If this is not done, the changes in the layout will not be seen until the
0359: * container is resized.
0360: *
0361: * @param i zero-based index of column to set. If this parameter is not
0362: * valid, an ArrayOutOfBoundsException will be thrown.
0363: * @param size width of the column. This parameter cannot be null.
0364: *
0365: * @see #getColumn
0366: */
0367:
0368: public void setColumn(int i, double size) {
0369: // Make sure size is valid
0370: if ((size < 0.0) && (size != FILL) && (size != PREFERRED)
0371: && (size != MINIMUM)) {
0372: size = 0.0;
0373: }
0374:
0375: // Copy new size
0376: columnSpec[i] = size;
0377:
0378: // Indicate that the cell sizes are not known
0379: dirty = true;
0380: }
0381:
0382: /**
0383: * Adjusts the height of a single row in this layout. After calling this
0384: * method, the caller should request this layout manager to perform the
0385: * layout. This can be done with the following code:
0386: *
0387: * <code>
0388: * layout.layoutContainer(container);
0389: * container.repaint();
0390: * </code>
0391: *
0392: * or
0393: *
0394: * <pre>
0395: * window.pack()
0396: * </pre>
0397: *
0398: * If this is not done, the changes in the layout will not be seen until the
0399: * container is resized.
0400: *
0401: * @param i zero-based index of row to set. If this parameter is not
0402: * valid, an ArrayOutOfBoundsException will be thrown.
0403: * @param size height of the row. This parameter cannot be null.
0404: *
0405: * @see #getRow
0406: */
0407:
0408: public void setRow(int i, double size) {
0409: // Make sure size is valid
0410: if ((size < 0.0) && (size != FILL) && (size != PREFERRED)
0411: && (size != MINIMUM)) {
0412: size = 0.0;
0413: }
0414:
0415: // Copy new size
0416: rowSpec[i] = size;
0417:
0418: // Indicate that the cell sizes are not known
0419: dirty = true;
0420: }
0421:
0422: /**
0423: * Gets the sizes of columns in this layout.
0424: *
0425: * @return widths of each of the columns
0426: *
0427: * @see #setColumn
0428: */
0429:
0430: public double[] getColumn() {
0431: // Copy columns
0432: double column[] = new double[columnSpec.length];
0433: System.arraycopy(columnSpec, 0, column, 0, column.length);
0434:
0435: return column;
0436: }
0437:
0438: /**
0439: * Gets the height of a single row in this layout.
0440: *
0441: * @return height of the requested row
0442: *
0443: * @see #setRow
0444: */
0445:
0446: public double[] getRow() {
0447: // Copy rows
0448: double row[] = new double[rowSpec.length];
0449: System.arraycopy(rowSpec, 0, row, 0, row.length);
0450:
0451: return row;
0452: }
0453:
0454: /**
0455: * Gets the width of a single column in this layout.
0456: *
0457: * @param i zero-based index of row to get. If this parameter is not valid,
0458: * an ArrayOutOfBoundsException will be thrown.
0459: *
0460: * @return width of the requested column
0461: *
0462: * @see #setRow
0463: */
0464:
0465: public double getColumn(int i) {
0466: return columnSpec[i];
0467: }
0468:
0469: /**
0470: * Gets the sizes of a row in this layout.
0471: *
0472: * @param i zero-based index of row to get. If this parameter is not valid,
0473: * an ArrayOutOfBoundsException will be thrown.
0474: *
0475: * @return height of each of the requested row
0476: *
0477: * @see #setRow
0478: */
0479:
0480: public double getRow(int i) {
0481: return rowSpec[i];
0482: }
0483:
0484: /**
0485: * Gets the number of columns in this layout.
0486: *
0487: * @return the number of columns
0488: */
0489:
0490: public int getNumColumn() {
0491: return columnSpec.length;
0492: }
0493:
0494: /**
0495: * Gets the number of rows in this layout.
0496: *
0497: * @return the number of rows
0498: */
0499:
0500: public int getNumRow() {
0501: return rowSpec.length;
0502: }
0503:
0504: //******************************************************************************
0505: //** Insertion/Deletion methods ***
0506: //******************************************************************************
0507:
0508: /**
0509: * Inserts a column in this layout. All components to the right of the
0510: * insertion point are moved right one column. The container will need to
0511: * be laid out after this method returns. See <code>setColumn</code>.
0512: *
0513: * @param i zero-based index at which to insert the column.
0514: * @param size size of the column to be inserted
0515: *
0516: * @see #setColumn
0517: * @see #deleteColumn
0518: */
0519:
0520: public void insertColumn(int i, double size) {
0521: // Make sure position is valid
0522: if ((i < 0) || (i > columnSpec.length))
0523: throw new IllegalArgumentException(
0524: "Parameter i is invalid. i = " + i
0525: + ". Valid range is [0, "
0526: + columnSpec.length + "].");
0527:
0528: // Make sure column size is valid
0529: if ((size < 0.0) && (size != FILL) && (size != PREFERRED)
0530: && (size != MINIMUM)) {
0531: size = 0.0;
0532: }
0533:
0534: // Copy columns
0535: double column[] = new double[columnSpec.length + 1];
0536: System.arraycopy(columnSpec, 0, column, 0, i);
0537: System.arraycopy(columnSpec, i, column, i + 1,
0538: columnSpec.length - i);
0539:
0540: // Insert column
0541: column[i] = size;
0542: columnSpec = column;
0543:
0544: // Move all components that are to the right of new row
0545: ListIterator iterator = list.listIterator(0);
0546:
0547: while (iterator.hasNext()) {
0548: // Get next entry
0549: Entry entry = (Entry) iterator.next();
0550:
0551: // Is the first column to the right of the new column
0552: if (entry.col1 >= i)
0553: // Move first column
0554: entry.col1++;
0555:
0556: // Is the second column to the right of the new column
0557: if (entry.col2 >= i)
0558: // Move second column
0559: entry.col2++;
0560: }
0561:
0562: // Indicate that the cell sizes are not known
0563: dirty = true;
0564: }
0565:
0566: /**
0567: * Inserts a row in this layout. All components below the insertion point
0568: * are moved down one row. The container will need to be laid out after this
0569: * method returns. See <code>setRow</code>.
0570: *
0571: * @param i zero-based index at which to insert the column.
0572: * @param size size of the row to be inserted
0573: *
0574: * @see #setRow
0575: * @see #deleteRow
0576: */
0577:
0578: public void insertRow(int i, double size) {
0579: // Make sure position is valid
0580: if ((i < 0) || (i > rowSpec.length))
0581: throw new IllegalArgumentException(
0582: "Parameter i is invalid. i = " + i
0583: + ". Valid range is [0, " + rowSpec.length
0584: + "].");
0585:
0586: // Make sure row size is valid
0587: if ((size < 0.0) && (size != FILL) && (size != PREFERRED)
0588: && (size != MINIMUM)) {
0589: size = 0.0;
0590: }
0591:
0592: // Copy rows
0593: double row[] = new double[rowSpec.length + 1];
0594: System.arraycopy(rowSpec, 0, row, 0, i);
0595: System.arraycopy(rowSpec, i, row, i + 1, rowSpec.length - i);
0596:
0597: // Insert row
0598: row[i] = size;
0599: rowSpec = row;
0600:
0601: // Move all components that are below the new row
0602: ListIterator iterator = list.listIterator(0);
0603:
0604: while (iterator.hasNext()) {
0605: // Get next entry
0606: Entry entry = (Entry) iterator.next();
0607:
0608: // Is the first row to the right of the new row
0609: if (entry.row1 >= i)
0610: // Move first row
0611: entry.row1++;
0612:
0613: // Is the second row to the right of the new row
0614: if (entry.row2 >= i)
0615: // Move second row
0616: entry.row2++;
0617: }
0618:
0619: // Indicate that the cell sizes are not known
0620: dirty = true;
0621: }
0622:
0623: /**
0624: * Deletes a column in this layout. All components to the right of the
0625: * deletion point are moved left one column. The container will need to
0626: * be laid out after this method returns. See <code>setColumn</code>.
0627: *
0628: * @param i zero-based index of column to delete
0629: *
0630: * @see #setColumn
0631: * @see #deleteColumn
0632: */
0633:
0634: public void deleteColumn(int i) {
0635: // Make sure position is valid
0636: if ((i < 0) || (i >= columnSpec.length))
0637: throw new IllegalArgumentException(
0638: "Parameter i is invalid. i = " + i
0639: + ". Valid range is [0, "
0640: + (columnSpec.length - 1) + "].");
0641:
0642: // Copy columns
0643: double column[] = new double[columnSpec.length - 1];
0644: System.arraycopy(columnSpec, 0, column, 0, i);
0645: System.arraycopy(columnSpec, i + 1, column, i,
0646: columnSpec.length - i - 1);
0647:
0648: // Delete column
0649: columnSpec = column;
0650:
0651: // Move all components that are to the right of row deleted
0652: ListIterator iterator = list.listIterator(0);
0653:
0654: while (iterator.hasNext()) {
0655: // Get next entry
0656: Entry entry = (Entry) iterator.next();
0657:
0658: // Is the first column to the right of the new column
0659: if (entry.col1 >= i)
0660: // Move first column
0661: entry.col1--;
0662:
0663: // Is the second column to the right of the new column
0664: if (entry.col2 >= i)
0665: // Move second column
0666: entry.col2--;
0667: }
0668:
0669: // Indicate that the cell sizes are not known
0670: dirty = true;
0671: }
0672:
0673: /**
0674: * Deletes a row in this layout. All components below the deletion point are
0675: * moved up one row. The container will need to be laid out after this method
0676: * returns. See <code>setRow</code>. There must be at least two rows in order
0677: * to delete a row.
0678: *
0679: * @param i zero-based index of column to delete
0680: *
0681: * @see #setRow
0682: * @see #deleteRow
0683: */
0684:
0685: public void deleteRow(int i) {
0686: // Make sure position is valid
0687: if ((i < 0) || (i >= rowSpec.length))
0688: throw new IllegalArgumentException(
0689: "Parameter i is invalid. i = " + i
0690: + ". Valid range is [0, "
0691: + (rowSpec.length - 1) + "].");
0692:
0693: // Copy rows
0694: double row[] = new double[rowSpec.length - 1];
0695: System.arraycopy(rowSpec, 0, row, 0, i);
0696: System
0697: .arraycopy(rowSpec, i + 1, row, i, rowSpec.length - i
0698: - 1);
0699:
0700: // Delete row
0701: rowSpec = row;
0702:
0703: // Move all components that are to below the row deleted
0704: ListIterator iterator = list.listIterator(0);
0705:
0706: while (iterator.hasNext()) {
0707: // Get next entry
0708: Entry entry = (Entry) iterator.next();
0709:
0710: // Is the first row below the new row
0711: if (entry.row1 >= i)
0712: // Move first row
0713: entry.row1--;
0714:
0715: // Is the second row below the new row
0716: if (entry.row2 >= i)
0717: // Move second row
0718: entry.row2--;
0719: }
0720:
0721: // Indicate that the cell sizes are not known
0722: dirty = true;
0723: }
0724:
0725: //******************************************************************************
0726: //** Misc methods ***
0727: //******************************************************************************
0728:
0729: /**
0730: * Converts this TableLayout to a string.
0731: *
0732: * @return a string representing the columns and row sizes in the form
0733: * "{{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}"
0734: */
0735:
0736: public String toString() {
0737: int counter;
0738:
0739: String value = "TableLayout {{";
0740:
0741: if (columnSpec.length > 0) {
0742: for (counter = 0; counter < columnSpec.length - 1; counter++)
0743: value += columnSpec[counter] + ", ";
0744:
0745: value += columnSpec[columnSpec.length - 1] + "}, {";
0746: } else
0747: value += "}, {";
0748:
0749: if (rowSpec.length > 0) {
0750: for (counter = 0; counter < rowSpec.length - 1; counter++)
0751: value += rowSpec[counter] + ", ";
0752:
0753: value += rowSpec[rowSpec.length - 1] + "}}";
0754: } else
0755: value += "}}";
0756:
0757: return value;
0758: }
0759:
0760: /**
0761: * Draws a grid on the given container. This is useful for seeing where the
0762: * rows and columns go. In the container's paint method, call this method.
0763: *
0764: * @param container container using this TableLayout
0765: * @param g graphics content of container (can be offscreen)
0766: */
0767:
0768: public void drawGrid(Container container, Graphics g) {
0769:
0770: // Calculate the sizes of the rows and columns
0771: Dimension d = container.getSize();
0772:
0773: if (dirty || (d.width != oldWidth) || (d.height != oldHeight))
0774: calculateSize(container);
0775:
0776: // Initialize y
0777: int y = 0;
0778:
0779: for (int row = 0; row < rowSize.length; row++) {
0780: // Initialize x
0781: int x = 0;
0782:
0783: for (int column = 0; column < columnSize.length; column++) {
0784: // Use a random color to make things easy to see
0785: Color color = new Color(
0786: (int) (Math.random() * 0xFFFFFFL));
0787: g.setColor(color);
0788:
0789: // Draw the cell as a solid rectangle
0790: g.fillRect(x, y, columnSize[column], rowSize[row]);
0791:
0792: // Increment x
0793: x += columnSize[column];
0794: }
0795:
0796: // Increment y
0797: y += rowSize[row];
0798: }
0799: }
0800:
0801: /**
0802: * Determines whether or not there are any hidden components. A hidden
0803: * component is one that will not be shown with this layout's current
0804: * configuration. Such a component is, at least partly, in an invalid row
0805: * or column. For example, on a table with five rows, row -1 and row 5 are both
0806: * invalid. Valid rows are 0 through 4, inclusively.
0807: *
0808: * @return True, if there are any hidden components. False, otherwise.
0809: *
0810: * @see #overlapping
0811: */
0812:
0813: public boolean hidden() {
0814: // Assume no components are hidden
0815: boolean hidden = false;
0816:
0817: // Check all components
0818: ListIterator iterator = list.listIterator(0);
0819:
0820: while (iterator.hasNext()) {
0821: // Get next entry
0822: Entry entry = (Entry) iterator.next();
0823:
0824: // Is this component valid
0825: if ((entry.row1 < 0) || (entry.col1 < 0)
0826: || (entry.row2 > rowSpec.length)
0827: || (entry.col2 > columnSpec.length)) {
0828: hidden = true;
0829: break;
0830: }
0831: }
0832:
0833: return hidden;
0834: }
0835:
0836: /**
0837: * Determines whether or not there are any overlapping components. Two
0838: * components overlap if they cover at least one common cell.
0839: *
0840: * @return True, if there are any overlapping components. False, otherwise.
0841: *
0842: * @see #hidden
0843: */
0844:
0845: public boolean overlapping() {
0846: // Count contraints
0847: int numEntry = list.size();
0848:
0849: // If there are no components, they can't be overlapping
0850: if (numEntry == 0)
0851: return false;
0852:
0853: // Assume no components are overlapping
0854: boolean overlapping = false;
0855:
0856: // Put entries in an array
0857: Entry entry[] = (Entry[]) list.toArray(new Entry[numEntry]);
0858:
0859: // Check all components
0860: for (int knowUnique = 1; knowUnique < numEntry; knowUnique++)
0861: for (int checking = knowUnique - 1; checking >= 0; checking--)
0862: if (((entry[checking].col1 >= entry[knowUnique].col1)
0863: && (entry[checking].col1 <= entry[knowUnique].col2)
0864: && (entry[checking].row1 >= entry[knowUnique].row1) && (entry[checking].row1 <= entry[knowUnique].row2))
0865: || ((entry[checking].col2 >= entry[knowUnique].col1)
0866: && (entry[checking].col2 <= entry[knowUnique].col2)
0867: && (entry[checking].row2 >= entry[knowUnique].row1) && (entry[checking].row2 <= entry[knowUnique].row2))) {
0868: overlapping = true;
0869: break;
0870: }
0871:
0872: return overlapping;
0873: }
0874:
0875: /**
0876: * Calculates the sizes of the rows and columns based on the absolute and
0877: * relative sizes specified in <code>rowSpec</code> and <code>columnSpec</code>
0878: * and the size of the container. The result is stored in <code>rowSize</code>
0879: * and <code>columnSize</code>.
0880: *
0881: * @param container container using this TableLayout
0882: */
0883:
0884: protected void calculateSize(Container container) {
0885: int counter; // Counting variable;
0886:
0887: // Get number of rows and columns
0888: int numColumn = columnSpec.length;
0889: int numRow = rowSpec.length;
0890:
0891: // Create array to hold actual sizes in pixels
0892: columnSize = new int[numColumn];
0893: rowSize = new int[numRow];
0894:
0895: // Get the container's insets
0896: Insets inset = container.getInsets();
0897:
0898: // Get the size of the container's available space
0899: Dimension d = container.getSize();
0900: int totalWidth = d.width - inset.left - inset.right;
0901: int totalHeight = d.height - inset.top - inset.bottom;
0902:
0903: // Initially, the available space is the total space
0904: int availableWidth = totalWidth;
0905: int availableHeight = totalHeight;
0906:
0907: // Assign absolute widths; this reduces available width
0908: for (counter = 0; counter < numColumn; counter++)
0909: // Is the current column an absolue size
0910: if ((columnSpec[counter] >= 1.0)
0911: || (columnSpec[counter] == 0.0)) {
0912: // Assign absolute width
0913: columnSize[counter] = (int) (columnSpec[counter] + 0.5);
0914:
0915: // Reduce available width
0916: availableWidth -= columnSize[counter];
0917: }
0918:
0919: // Assign absolute heights; this reduces available height
0920: for (counter = 0; counter < numRow; counter++)
0921: // Is the current column an absolue size
0922: if ((rowSpec[counter] >= 1.0) || (rowSpec[counter] == 0.0)) {
0923: // Assign absolute width
0924: rowSize[counter] = (int) (rowSpec[counter] + 0.5);
0925:
0926: // Reduce available width
0927: availableHeight -= rowSize[counter];
0928: }
0929:
0930: // Assign preferred and minimum widths; this reduces available width.
0931: // Assignment of preferred/minimum with is like assignment of absolute
0932: // widths except that each column must determine the maximum
0933: // preferred/minimum width of the components that are completely contained
0934: // within the column.
0935: for (counter = 0; counter < numColumn; counter++)
0936: // Is the current column a preferred size
0937: if ((columnSpec[counter] == PREFERRED)
0938: || (columnSpec[counter] == MINIMUM)) {
0939: // Assume a maximum width of zero
0940: int maxWidth = 0;
0941:
0942: // Find maximum preferred width of all components completely
0943: // contained within this column
0944: ListIterator iterator = list.listIterator(0);
0945:
0946: while (iterator.hasNext()) {
0947: Entry entry = (Entry) iterator.next();
0948:
0949: if ((entry.col1 == counter)
0950: && (entry.col2 == counter)) {
0951: Dimension p = (columnSpec[counter] == PREFERRED) ? entry.component
0952: .getPreferredSize()
0953: : entry.component.getMinimumSize();
0954:
0955: int width = (p == null) ? 0 : p.width;
0956:
0957: if (maxWidth < width)
0958: maxWidth = width;
0959: }
0960: }
0961:
0962: // Assign preferred width
0963: columnSize[counter] = maxWidth;
0964:
0965: // Reduce available width
0966: availableWidth -= maxWidth;
0967: }
0968:
0969: // Assign preferred and minimum heights; this reduces available height.
0970: // Assignment of preferred/minimum with is like assignment of absolute
0971: // heights except that each row must determine the maximum
0972: // preferred/minimum height of the components that are completely contained
0973: // within the row.
0974: for (counter = 0; counter < numRow; counter++)
0975: // Is the current row a preferred size
0976: if ((rowSpec[counter] == PREFERRED)
0977: || (rowSpec[counter] == MINIMUM)) {
0978: // Assume a maximum height of zero
0979: int maxHeight = 0;
0980:
0981: // Find maximum preferred height of all components completely
0982: // contained within this row
0983: ListIterator iterator = list.listIterator(0);
0984:
0985: while (iterator.hasNext()) {
0986: Entry entry = (Entry) iterator.next();
0987:
0988: if ((entry.row1 == counter)
0989: && (entry.row2 == counter)) {
0990: Dimension p = (rowSpec[counter] == PREFERRED) ? entry.component
0991: .getPreferredSize()
0992: : entry.component.getMinimumSize();
0993:
0994: int height = (p == null) ? 0 : p.height;
0995:
0996: if (maxHeight < height)
0997: maxHeight = height;
0998: }
0999: }
1000:
1001: // Assign preferred height
1002: rowSize[counter] = maxHeight;
1003:
1004: // Reduce available height
1005: availableHeight -= maxHeight;
1006: }
1007:
1008: // Remember how much space is available for relatively sized cells
1009: int relativeWidth = availableWidth;
1010: int relativeHeight = availableHeight;
1011:
1012: // Make sure relativeWidth and relativeHeight are non-negative
1013: if (relativeWidth < 0)
1014: relativeWidth = 0;
1015:
1016: if (relativeHeight < 0)
1017: relativeHeight = 0;
1018:
1019: // Assign relative widths
1020: for (counter = 0; counter < numColumn; counter++)
1021: // Is the current column an relative size
1022: if ((columnSpec[counter] > 0.0)
1023: && (columnSpec[counter] < 1.0)) {
1024: // Assign relative width
1025: columnSize[counter] = (int) (columnSpec[counter]
1026: * relativeWidth + 0.5);
1027:
1028: // Reduce available width
1029: availableWidth -= columnSize[counter];
1030: }
1031:
1032: // Assign relative widths
1033: for (counter = 0; counter < numRow; counter++)
1034: // Is the current column an relative size
1035: if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0)) {
1036: // Assign relative width
1037: rowSize[counter] = (int) (rowSpec[counter]
1038: * relativeHeight + 0.5);
1039:
1040: // Reduce available width
1041: availableHeight -= rowSize[counter];
1042: }
1043:
1044: // Make sure availableWidth and availableHeight are non-negative
1045: if (availableWidth < 0)
1046: availableWidth = 0;
1047:
1048: if (availableHeight < 0)
1049: availableHeight = 0;
1050:
1051: // Count the number of "fill" cells
1052: int numFillWidth = 0;
1053: int numFillHeight = 0;
1054:
1055: for (counter = 0; counter < numColumn; counter++)
1056: if (columnSpec[counter] == FILL)
1057: numFillWidth++;
1058:
1059: for (counter = 0; counter < numRow; counter++)
1060: if (rowSpec[counter] == FILL)
1061: numFillHeight++;
1062:
1063: // If numFillWidth (numFillHeight) is zero, the cooresponding if statements
1064: // will always evaluate to false and the division will not occur.
1065:
1066: // If there are more than one "fill" cell, slack may occur due to rounding
1067: // errors
1068: int slackWidth = availableWidth;
1069: int slackHeight = availableHeight;
1070:
1071: // Assign "fill" cells equal amounts of the remaining space
1072: for (counter = 0; counter < numColumn; counter++)
1073: if (columnSpec[counter] == FILL) {
1074: columnSize[counter] = availableWidth / numFillWidth;
1075: slackWidth -= columnSize[counter];
1076: }
1077:
1078: for (counter = 0; counter < numRow; counter++)
1079: if (rowSpec[counter] == FILL) {
1080: rowSize[counter] = availableHeight / numFillHeight;
1081: slackHeight -= rowSize[counter];
1082: }
1083:
1084: // Add slack to the last "fill" cell
1085: for (counter = numColumn - 1; counter >= 0; counter--) {
1086: if (columnSpec[counter] == FILL) {
1087: columnSize[counter] += slackWidth;
1088: break;
1089: }
1090: }
1091:
1092: for (counter = numRow - 1; counter >= 0; counter--) {
1093: if (rowSpec[counter] == FILL) {
1094: rowSize[counter] += slackHeight;
1095: break;
1096: }
1097: }
1098:
1099: // Calculate offsets of each column (done for effeciency)
1100: columnOffset = new int[numColumn + 1];
1101: columnOffset[0] = inset.left;
1102:
1103: for (counter = 0; counter < numColumn; counter++)
1104: columnOffset[counter + 1] = columnOffset[counter]
1105: + columnSize[counter];
1106:
1107: // Calculate offsets of each row (done for effeciency)
1108: rowOffset = new int[numRow + 1];
1109: rowOffset[0] = inset.top;
1110:
1111: for (counter = 0; counter < numRow; counter++)
1112: rowOffset[counter + 1] = rowOffset[counter]
1113: + rowSize[counter];
1114:
1115: // Indicate that the size of the cells are known for the container's
1116: // current size
1117: dirty = false;
1118: oldWidth = totalWidth;
1119: oldHeight = totalHeight;
1120: }
1121:
1122: //******************************************************************************
1123: //** java.awt.event.LayoutManager methods ***
1124: //******************************************************************************
1125:
1126: /**
1127: * To lay out the specified container using this layout. This method reshapes
1128: * the components in the specified target container in order to satisfy the
1129: * constraints of all components.
1130: *
1131: * <p>User code should not have to call this method directly.</p>
1132: *
1133: * @param container container being served by this layout manager
1134: */
1135:
1136: public void layoutContainer(Container container) {
1137: int x, y; // Coordinates of the currnet component in pixels
1138: int w, h; // Width and height of the current component in pixels
1139:
1140: // Calculate sizes if container has changed size or components were added
1141: Dimension d = container.getSize();
1142:
1143: if (dirty || (d.width != oldWidth) || (d.height != oldHeight))
1144: calculateSize(container);
1145:
1146: // Get components
1147: Component component[] = container.getComponents();
1148:
1149: // Layout components
1150: for (int counter = 0; counter < component.length; counter++) {
1151: try {
1152: // Get the entry entry for the next component
1153: ListIterator iterator = list.listIterator(0);
1154: Entry entry = null;
1155:
1156: while (iterator.hasNext()) {
1157: entry = (Entry) iterator.next();
1158:
1159: if (entry.component == component[counter])
1160: break;
1161: else
1162: entry = null;
1163: }
1164:
1165: // Skip any components that have not been place in a specific cell
1166: if (entry == null)
1167: break;
1168:
1169: // Does the entry occupy a single cell
1170: if (entry.singleCell) {
1171: // The following block of code has been optimized so that the
1172: // preferred size of the component is only obtained if it is
1173: // needed. There are components in which the getPreferredSize
1174: // method is extremely expensive, such as data driven controls
1175: // with a large amount of data.
1176:
1177: // Get the preferred size of the component
1178: int preferredWidth = 0;
1179: int preferredHeight = 0;
1180:
1181: if ((entry.hAlign != FULL)
1182: || (entry.vAlign != FULL)) {
1183: Dimension preferredSize = component[counter]
1184: .getPreferredSize();
1185:
1186: preferredWidth = preferredSize.width;
1187: preferredHeight = preferredSize.height;
1188: }
1189:
1190: // Determine cell width and height
1191: int cellWidth = columnSize[entry.col1];
1192: int cellHeight = rowSize[entry.row1];
1193:
1194: // Determine the width of the component
1195: if ((entry.hAlign == FULL)
1196: || (cellWidth < preferredWidth))
1197: // Use the width of the cell
1198: w = cellWidth;
1199: else
1200: // Use the prefered width of the component
1201: w = preferredWidth;
1202:
1203: // Determine left and right boarders
1204: switch (entry.hAlign) {
1205: case LEFT:
1206: // Align left side along left edge of cell
1207: x = columnOffset[entry.col1];
1208: break;
1209:
1210: case RIGHT:
1211: // Align right side along right edge of cell
1212: x = columnOffset[entry.col1 + 1] - w;
1213: break;
1214:
1215: case CENTER:
1216: // Center justify component
1217: x = columnOffset[entry.col1]
1218: + ((cellWidth - w) >> 1);
1219: break;
1220:
1221: case FULL:
1222: // Align left side along left edge of cell
1223: x = columnOffset[entry.col1];
1224: break;
1225:
1226: default:
1227: // This is a never should happen case, but just in case
1228: x = 0;
1229: }
1230:
1231: // Determine the height of the component
1232: if ((entry.vAlign == FULL)
1233: || (cellHeight < preferredHeight))
1234: // Use the height of the cell
1235: h = cellHeight;
1236: else
1237: // Use the prefered height of the component
1238: h = preferredHeight;
1239:
1240: // Determine top and bottom boarders
1241: switch (entry.vAlign) {
1242: case TOP:
1243: // Align top side along top edge of cell
1244: y = rowOffset[entry.row1];
1245: break;
1246:
1247: case BOTTOM:
1248: // Align right side along right edge of cell
1249: y = rowOffset[entry.row1 + 1] - h;
1250: break;
1251:
1252: case CENTER:
1253: // Center justify component
1254: y = rowOffset[entry.row1]
1255: + ((cellHeight - h) >> 1);
1256: break;
1257:
1258: case FULL:
1259: // Align right side along right edge of cell
1260: y = rowOffset[entry.row1];
1261: break;
1262:
1263: default:
1264: // This is a never should happen case, but just in case
1265: y = 0;
1266: }
1267: } else {
1268: // Align left side with left boarder of first column
1269: x = columnOffset[entry.col1];
1270:
1271: // Align top side along top edge of first row
1272: y = rowOffset[entry.row1];
1273:
1274: // Align right side with right boarder of second column
1275: w = columnOffset[entry.col2 + 1]
1276: - columnOffset[entry.col1];
1277:
1278: // Align bottom side with bottom boarder of second row
1279: h = rowOffset[entry.row2 + 1]
1280: - rowOffset[entry.row1];
1281: }
1282:
1283: // Move and resize component
1284: component[counter].setBounds(x, y, w, h);
1285: } catch (Exception error) {
1286: // If any error occurs, skip this component
1287: continue;
1288: }
1289: }
1290: }
1291:
1292: /**
1293: * Determines the preferred size of the container argument using this layout.
1294: * The preferred size is the smallest size that, if used for the container's
1295: * size, will ensure that all components are at least as large as their
1296: * preferred size. This method cannot guarantee that all components will be
1297: * their preferred size. For example, if component A and component B are each
1298: * allocate half of the container's width and component A wants to be 10 pixels
1299: * wide while component B wants to be 100 pixels wide, they cannot both be
1300: * accommodated. Since in general components rather be larger than their
1301: * preferred size instead of smaller, component B's request will be fulfilled.
1302: * The preferred size of the container would be 200 pixels.
1303: *
1304: * @param container container being served by this layout manager
1305: *
1306: * @return a dimension indicating the container's preferred size
1307: */
1308:
1309: public Dimension preferredLayoutSize(Container container) {
1310: Dimension size; // Preferred size of current component
1311: int scaledWidth = 0; // Preferred width of scalled components
1312: int scaledHeight = 0; // Preferred height of scalled components
1313: int temp; // Temporary variable used to compare sizes
1314: int counter; // Counting variable
1315:
1316: // Determine percentage of space allocated to fill components. This is
1317: // one minus the sum of all scalable components.
1318: double fillWidthRatio = 1.0;
1319: double fillHeightRatio = 1.0;
1320: int numFillWidth = 0;
1321: int numFillHeight = 0;
1322:
1323: for (counter = 0; counter < columnSpec.length; counter++)
1324: if ((columnSpec[counter] > 0.0)
1325: && (columnSpec[counter] < 1.0))
1326: fillWidthRatio -= columnSpec[counter];
1327: else if (columnSpec[counter] == FILL)
1328: numFillWidth++;
1329:
1330: for (counter = 0; counter < rowSpec.length; counter++)
1331: if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0))
1332: fillHeightRatio -= rowSpec[counter];
1333: else if (rowSpec[counter] == FILL)
1334: numFillHeight++;
1335:
1336: // Adjust fill ratios to reflect number of fill rows/columns
1337: if (numFillWidth > 1)
1338: fillWidthRatio /= numFillWidth;
1339:
1340: if (numFillHeight > 1)
1341: fillHeightRatio /= numFillHeight;
1342:
1343: // Cap fill ratio bottoms to 0.0
1344: if (fillWidthRatio < 0.0)
1345: fillWidthRatio = 0.0;
1346:
1347: if (fillHeightRatio < 0.0)
1348: fillHeightRatio = 0.0;
1349:
1350: // Calculate preferred/minimum column widths
1351: int columnPrefMin[] = new int[columnSpec.length];
1352:
1353: for (counter = 0; counter < columnSpec.length; counter++)
1354: // Is the current column a preferred/minimum size
1355: if ((columnSpec[counter] == PREFERRED)
1356: || (columnSpec[counter] == MINIMUM)) {
1357: // Assume a maximum width of zero
1358: int maxWidth = 0;
1359:
1360: // Find maximum preferred/minimum width of all components completely
1361: // contained within this column
1362: ListIterator iterator = list.listIterator(0);
1363:
1364: while (iterator.hasNext()) {
1365: Entry entry = (Entry) iterator.next();
1366:
1367: if ((entry.col1 == counter)
1368: && (entry.col2 == counter)) {
1369: Dimension p = (columnSpec[counter] == PREFERRED) ? entry.component
1370: .getPreferredSize()
1371: : entry.component.getMinimumSize();
1372:
1373: int width = (p == null) ? 0 : p.width;
1374:
1375: if (maxWidth < width)
1376: maxWidth = width;
1377: }
1378: }
1379:
1380: // Set column's preferred/minimum width
1381: columnPrefMin[counter] = maxWidth;
1382: }
1383:
1384: // Calculate preferred/minimum row heights
1385: int rowPrefMin[] = new int[rowSpec.length];
1386:
1387: for (counter = 0; counter < rowSpec.length; counter++)
1388: // Is the current row a preferred/minimum size
1389: if ((rowSpec[counter] == PREFERRED)
1390: || (rowSpec[counter] == MINIMUM)) {
1391: // Assume a maximum height of zero
1392: int maxHeight = 0;
1393:
1394: // Find maximum preferred height of all components completely
1395: // contained within this row
1396: ListIterator iterator = list.listIterator(0);
1397:
1398: while (iterator.hasNext()) {
1399: Entry entry = (Entry) iterator.next();
1400:
1401: if ((entry.row1 == counter)
1402: && (entry.row1 == counter)) {
1403: Dimension p = (rowSpec[counter] == PREFERRED) ? entry.component
1404: .getPreferredSize()
1405: : entry.component.getMinimumSize();
1406:
1407: int height = (p == null) ? 0 : p.height;
1408:
1409: if (maxHeight < height)
1410: maxHeight = height;
1411: }
1412: }
1413:
1414: // Add preferred height
1415: rowPrefMin[counter] += maxHeight;
1416: }
1417:
1418: // Find maximum preferred size of all scaled components
1419: ListIterator iterator = list.listIterator(0);
1420:
1421: while (iterator.hasNext()) {
1422: // Get next entry
1423: Entry entry = (Entry) iterator.next();
1424:
1425: // Make sure entry is in valid rows and columns
1426: if ((entry.col1 < 0) || (entry.col1 >= columnSpec.length)
1427: || (entry.col2 >= columnSpec.length)
1428: || (entry.row1 < 0)
1429: || (entry.row1 >= rowSpec.length)
1430: || (entry.row2 >= rowSpec.length)) {
1431: // Skip the bad component
1432: continue;
1433: }
1434:
1435: // Get preferred size of current component
1436: size = entry.component.getPreferredSize();
1437:
1438: // Calculate portion of component that is not absolutely sized
1439: int scalableWidth = size.width;
1440: int scalableHeight = size.height;
1441:
1442: for (counter = entry.col1; counter <= entry.col2; counter++)
1443: if (columnSpec[counter] >= 1.0)
1444: scalableWidth -= columnSpec[counter];
1445: else if ((columnSpec[counter] == PREFERRED)
1446: || (columnSpec[counter] == MINIMUM)) {
1447: scalableWidth -= columnPrefMin[counter];
1448: }
1449:
1450: for (counter = entry.row1; counter <= entry.row2; counter++)
1451: if (rowSpec[counter] >= 1.0)
1452: scalableHeight -= rowSpec[counter];
1453: else if ((rowSpec[counter] == PREFERRED)
1454: || (rowSpec[counter] == MINIMUM)) {
1455: scalableHeight -= rowPrefMin[counter];
1456: }
1457:
1458: //----------------------------------------------------------------------
1459:
1460: // Determine total percentage of scalable space that the component
1461: // occupies by adding the relative columns and the fill columns
1462: double relativeWidth = 0.0;
1463:
1464: for (counter = entry.col1; counter <= entry.col2; counter++) {
1465: // Column is scaled
1466: if ((columnSpec[counter] > 0.0)
1467: && (columnSpec[counter] < 1.0))
1468: // Add scaled size to relativeWidth
1469: relativeWidth += columnSpec[counter];
1470: // Column is fill
1471: else if ((columnSpec[counter] == FILL)
1472: && (fillWidthRatio != 0.0))
1473: // Add fill size to relativeWidth
1474: relativeWidth += fillWidthRatio;
1475: }
1476:
1477: // Determine the total scaled width as estimated by this component
1478: if (relativeWidth == 0)
1479: temp = 0;
1480: else
1481: temp = (int) (scalableWidth / relativeWidth + 0.5);
1482:
1483: // If the container needs to be bigger, make it so
1484: if (scaledWidth < temp)
1485: scaledWidth = temp;
1486:
1487: //----------------------------------------------------------------------
1488:
1489: // Determine total percentage of scalable space that the component
1490: // occupies by adding the relative columns and the fill columns
1491: double relativeHeight = 0.0;
1492:
1493: for (counter = entry.row1; counter <= entry.row2; counter++) {
1494: // Row is scaled
1495: if ((rowSpec[counter] > 0.0)
1496: && (rowSpec[counter] < 1.0))
1497: // Add scaled size to relativeHeight
1498: relativeHeight += rowSpec[counter];
1499: // Row is fill
1500: else if ((rowSpec[counter] == FILL)
1501: && (fillHeightRatio != 0.0))
1502: // Add fill size to relativeHeight
1503: relativeHeight += fillHeightRatio;
1504: }
1505:
1506: // Determine the total scaled width as estimated by this component
1507: if (relativeHeight == 0)
1508: temp = 0;
1509: else
1510: temp = (int) (scalableHeight / relativeHeight + 0.5);
1511:
1512: // If the container needs to be bigger, make it so
1513: if (scaledHeight < temp)
1514: scaledHeight = temp;
1515: }
1516:
1517: // totalWidth is the scaledWidth plus the sum of all absolute widths and all
1518: // preferred widths
1519: int totalWidth = scaledWidth;
1520:
1521: for (counter = 0; counter < columnSpec.length; counter++)
1522: // Is the current column an absolute size
1523: if (columnSpec[counter] >= 1.0)
1524: totalWidth += (int) (columnSpec[counter] + 0.5);
1525: // Is the current column a preferred/minimum size
1526: else if ((columnSpec[counter] == PREFERRED)
1527: || (columnSpec[counter] == MINIMUM)) {
1528: // Add preferred/minimum width
1529: totalWidth += columnPrefMin[counter];
1530: }
1531:
1532: // totalHeight is the scaledHeight plus the sum of all absolute heights and
1533: // all preferred widths
1534: int totalHeight = scaledHeight;
1535:
1536: for (counter = 0; counter < rowSpec.length; counter++)
1537: // Is the current row an absolute size
1538: if (rowSpec[counter] >= 1.0)
1539: totalHeight += (int) (rowSpec[counter] + 0.5);
1540: // Is the current row a preferred size
1541: else if ((rowSpec[counter] == PREFERRED)
1542: || (rowSpec[counter] == MINIMUM)) {
1543: // Add preferred/minimum width
1544: totalHeight += rowPrefMin[counter];
1545: }
1546:
1547: // Compensate for container's insets
1548: Insets inset = container.getInsets();
1549: totalWidth += inset.left + inset.right;
1550: totalHeight += inset.top + inset.bottom;
1551:
1552: return new Dimension(totalWidth, totalHeight);
1553: }
1554:
1555: /**
1556: * Determines the minimum size of the container argument using this layout.
1557: * The minimum size is the smallest size that, if used for the container's
1558: * size, will ensure that all components are at least as large as their
1559: * minimum size. This method cannot guarantee that all components will be
1560: * their minimum size. For example, if component A and component B are each
1561: * allocate half of the container's width and component A wants to be 10 pixels
1562: * wide while component B wants to be 100 pixels wide, they cannot both be
1563: * accommodated. Since in general components rather be larger than their
1564: * minimum size instead of smaller, component B's request will be fulfilled.
1565: * The minimum size of the container would be 200 pixels.
1566: *
1567: * @param container container being served by this layout manager
1568: *
1569: * @return a dimension indicating the container's minimum size
1570: */
1571:
1572: public Dimension minimumLayoutSize(Container container) {
1573: Dimension size; // Minimum size of current component
1574: int scaledWidth = 0; // Minimum width of scalled components
1575: int scaledHeight = 0; // Minimum height of scalled components
1576: int temp; // Temporary variable used to compare sizes
1577: int counter; // Counting variable
1578:
1579: // Determine percentage of space allocated to fill components. This is
1580: // one minus the sum of all scalable components.
1581: double fillWidthRatio = 1.0;
1582: double fillHeightRatio = 1.0;
1583: int numFillWidth = 0;
1584: int numFillHeight = 0;
1585:
1586: for (counter = 0; counter < columnSpec.length; counter++)
1587: if ((columnSpec[counter] > 0.0)
1588: && (columnSpec[counter] < 1.0))
1589: fillWidthRatio -= columnSpec[counter];
1590: else if (columnSpec[counter] == FILL)
1591: numFillWidth++;
1592:
1593: for (counter = 0; counter < rowSpec.length; counter++)
1594: if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0))
1595: fillHeightRatio -= rowSpec[counter];
1596: else if (rowSpec[counter] == FILL)
1597: numFillHeight++;
1598:
1599: // Adjust fill ratios to reflect number of fill rows/columns
1600: if (numFillWidth > 1)
1601: fillWidthRatio /= numFillWidth;
1602:
1603: if (numFillHeight > 1)
1604: fillHeightRatio /= numFillHeight;
1605:
1606: // Cap fill ratio bottoms to 0.0
1607: if (fillWidthRatio < 0.0)
1608: fillWidthRatio = 0.0;
1609:
1610: if (fillHeightRatio < 0.0)
1611: fillHeightRatio = 0.0;
1612:
1613: // Find maximum minimum size of all scaled components
1614: ListIterator iterator = list.listIterator(0);
1615:
1616: while (iterator.hasNext()) {
1617: // Get next entry
1618: Entry entry = (Entry) iterator.next();
1619:
1620: // Make sure entry is in valid rows and columns
1621: if ((entry.col1 < 0) || (entry.col1 >= columnSpec.length)
1622: || (entry.col2 >= columnSpec.length)
1623: || (entry.row1 < 0)
1624: || (entry.row1 >= rowSpec.length)
1625: || (entry.row2 >= rowSpec.length)) {
1626: // Skip the bad component
1627: continue;
1628: }
1629:
1630: // Get minimum size of current component
1631: size = entry.component.getMinimumSize();
1632:
1633: // Calculate portion of component that is not absolutely sized
1634: int scalableWidth = size.width;
1635: int scalableHeight = size.height;
1636:
1637: for (counter = entry.col1; counter <= entry.col2; counter++)
1638: if (columnSpec[counter] >= 1.0)
1639: scalableWidth -= columnSpec[counter];
1640:
1641: for (counter = entry.row1; counter <= entry.row2; counter++)
1642: if (rowSpec[counter] >= 1.0)
1643: scalableHeight -= rowSpec[counter];
1644:
1645: //----------------------------------------------------------------------
1646:
1647: // Determine total percentage of scalable space that the component
1648: // occupies by adding the relative columns and the fill columns
1649: double relativeWidth = 0.0;
1650:
1651: for (counter = entry.col1; counter <= entry.col2; counter++) {
1652: // Column is scaled
1653: if ((columnSpec[counter] > 0.0)
1654: && (columnSpec[counter] < 1.0))
1655: // Add scaled size to relativeWidth
1656: relativeWidth += columnSpec[counter];
1657: // Column is fill
1658: else if ((columnSpec[counter] == FILL)
1659: && (fillWidthRatio != 0.0))
1660: // Add fill size to relativeWidth
1661: relativeWidth += fillWidthRatio;
1662: }
1663:
1664: // Determine the total scaled width as estimated by this component
1665: if (relativeWidth == 0)
1666: temp = 0;
1667: else
1668: temp = (int) (scalableWidth / relativeWidth + 0.5);
1669:
1670: // If the container needs to be bigger, make it so
1671: if (scaledWidth < temp)
1672: scaledWidth = temp;
1673:
1674: //----------------------------------------------------------------------
1675:
1676: // Determine total percentage of scalable space that the component
1677: // occupies by adding the relative columns and the fill columns
1678: double relativeHeight = 0.0;
1679:
1680: for (counter = entry.row1; counter <= entry.row2; counter++) {
1681: // Row is scaled
1682: if ((rowSpec[counter] > 0.0)
1683: && (rowSpec[counter] < 1.0))
1684: // Add scaled size to relativeHeight
1685: relativeHeight += rowSpec[counter];
1686: // Row is fill
1687: else if ((rowSpec[counter] == FILL)
1688: && (fillHeightRatio != 0.0))
1689: // Add fill size to relativeHeight
1690: relativeHeight += fillHeightRatio;
1691: }
1692:
1693: // Determine the total scaled width as estimated by this component
1694: if (relativeHeight == 0)
1695: temp = 0;
1696: else
1697: temp = (int) (scalableHeight / relativeHeight + 0.5);
1698:
1699: // If the container needs to be bigger, make it so
1700: if (scaledHeight < temp)
1701: scaledHeight = temp;
1702: }
1703:
1704: // totalWidth is the scaledWidth plus the sum of all absolute widths and all
1705: // preferred widths
1706: int totalWidth = scaledWidth;
1707:
1708: for (counter = 0; counter < columnSpec.length; counter++)
1709: // Is the current column an absolute size
1710: if (columnSpec[counter] >= 1.0)
1711: totalWidth += (int) (columnSpec[counter] + 0.5);
1712: // Is the current column a preferred size
1713: else if ((columnSpec[counter] == PREFERRED)
1714: || (columnSpec[counter] == MINIMUM)) {
1715: // Assume a maximum width of zero
1716: int maxWidth = 0;
1717:
1718: // Find maximum preferred width of all components completely
1719: // contained within this column
1720: iterator = list.listIterator(0);
1721:
1722: while (iterator.hasNext()) {
1723: Entry entry = (Entry) iterator.next();
1724:
1725: if ((entry.col1 == counter)
1726: && (entry.col2 == counter)) {
1727: Dimension p = (columnSpec[counter] == PREFERRED) ? entry.component
1728: .getPreferredSize()
1729: : entry.component.getMinimumSize();
1730:
1731: int width = (p == null) ? 0 : p.width;
1732:
1733: if (maxWidth < width)
1734: maxWidth = width;
1735: }
1736: }
1737:
1738: // Add preferred width
1739: totalWidth += maxWidth;
1740: }
1741:
1742: // totalHeight is the scaledHeight plus the sum of all absolute heights and
1743: // all preferred widths
1744: int totalHeight = scaledHeight;
1745:
1746: for (counter = 0; counter < rowSpec.length; counter++)
1747: // Is the current row an absolute size
1748: if (rowSpec[counter] >= 1.0)
1749: totalHeight += (int) (rowSpec[counter] + 0.5);
1750: // Is the current row a preferred size
1751: else if ((rowSpec[counter] == PREFERRED)
1752: || (rowSpec[counter] == MINIMUM)) {
1753: // Assume a maximum height of zero
1754: int maxHeight = 0;
1755:
1756: // Find maximum preferred height of all components completely
1757: // contained within this row
1758: iterator = list.listIterator(0);
1759:
1760: while (iterator.hasNext()) {
1761: Entry entry = (Entry) iterator.next();
1762:
1763: if ((entry.row1 == counter)
1764: && (entry.row1 == counter)) {
1765: Dimension p = (rowSpec[counter] == PREFERRED) ? entry.component
1766: .getPreferredSize()
1767: : entry.component.getMinimumSize();
1768:
1769: int height = (p == null) ? 0 : p.height;
1770:
1771: if (maxHeight < height)
1772: maxHeight = height;
1773: }
1774: }
1775:
1776: // Add preferred height
1777: totalHeight += maxHeight;
1778: }
1779:
1780: // Compensate for container's insets
1781: Insets inset = container.getInsets();
1782: totalWidth += inset.left + inset.right;
1783: totalHeight += inset.top + inset.bottom;
1784:
1785: return new Dimension(totalWidth, totalHeight);
1786: }
1787:
1788: /**
1789: * Adds the specified component with the specified name to the layout.
1790: *
1791: * @param name indicates entry's position and anchor
1792: * @param component component to add
1793: */
1794:
1795: public void addLayoutComponent(String name, Component component) {
1796: addLayoutComponent(component, name);
1797: }
1798:
1799: //******************************************************************************
1800: //** java.awt.event.LayoutManager2 methods ***
1801: //******************************************************************************
1802:
1803: /**
1804: * Adds the specified component with the specified name to the layout.
1805: *
1806: * @param component component to add
1807: * @param constraint indicates entry's position and alignment
1808: */
1809:
1810: public void addLayoutComponent(Component component,
1811: Object constraint) {
1812: if (constraint instanceof String) {
1813: // Create an entry to associate component with its constraints
1814: constraint = new TableLayoutConstraints((String) constraint);
1815:
1816: // Add component and constraints to the list
1817: list.add(new Entry(component,
1818: (TableLayoutConstraints) constraint));
1819: } else if (constraint instanceof TableLayoutConstraints) {
1820: // Add component and constraints to the list
1821: list.add(new Entry(component,
1822: (TableLayoutConstraints) constraint));
1823: } else if (constraint == null)
1824: throw new IllegalArgumentException(
1825: "No constraint for the component");
1826: else
1827: throw new IllegalArgumentException(
1828: "Cannot accept a constraint of class "
1829: + constraint.getClass());
1830: }
1831:
1832: /**
1833: * Removes the specified component from the layout.
1834: *
1835: * @param component component being removed
1836: */
1837:
1838: public void removeLayoutComponent(Component component) {
1839: list.remove(component);
1840: }
1841:
1842: /**
1843: * Returns the maximum dimensions for this layout given the components in the
1844: * specified target container.
1845: *
1846: * @param target the component which needs to be laid out
1847: *
1848: * @return unconditionally, a Dimension of Integer.MAX_VALUE by
1849: * Integer.MAX_VALUE since TableLayout does not limit the
1850: * maximum size of a container
1851: */
1852:
1853: public Dimension maximumLayoutSize(Container target) {
1854: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1855: }
1856:
1857: /**
1858: * Returns the alignment along the x axis. This specifies how the component
1859: * would like to be aligned relative to other components. The value should be
1860: * a number between 0 and 1 where 0 represents alignment along the origin, 1 is
1861: * aligned the furthest away from the origin, 0.5 is centered, etc.
1862: *
1863: * @return unconditionally, 0.5
1864: */
1865:
1866: public float getLayoutAlignmentX(Container parent) {
1867: return 0.5f;
1868: }
1869:
1870: /**
1871: * Returns the alignment along the y axis. This specifies how the component
1872: * would like to be aligned relative to other components. The value should be
1873: * a number between 0 and 1 where 0 represents alignment along the origin, 1 is
1874: * aligned the furthest away from the origin, 0.5 is centered, etc.
1875: *
1876: * @return unconditionally, 0.5
1877: */
1878:
1879: public float getLayoutAlignmentY(Container parent) {
1880: return 0.5f;
1881: }
1882:
1883: /**
1884: * Invalidates the layout, indicating that if the layout manager has cached
1885: * information it should be discarded.
1886: */
1887:
1888: public void invalidateLayout(Container target) {
1889: dirty = true;
1890: }
1891:
1892: //******************************************************************************
1893: //*** Inner Class ***
1894: //******************************************************************************
1895:
1896: // The following inner class is used to bind components to their constraints
1897: protected class Entry extends TableLayoutConstraints {
1898: /** Component bound by the constraints */
1899: protected Component component;
1900:
1901: /** Does the component occupy a single cell */
1902: protected boolean singleCell;
1903:
1904: /**
1905: * Constructs an Entry that binds a component to a set of constraints.
1906: *
1907: * @param component component being bound
1908: * @param constraint constraints being applied
1909: */
1910:
1911: public Entry(Component component,
1912: TableLayoutConstraints constraint) {
1913: super (constraint.col1, constraint.row1, constraint.col2,
1914: constraint.row2, constraint.hAlign,
1915: constraint.vAlign);
1916:
1917: singleCell = ((row1 == row2) && (col1 == col2));
1918: this .component = component;
1919: }
1920:
1921: /**
1922: * Determines whether or not two entries are equal.
1923: *
1924: * @param object object being compared to; must be a Component if it
1925: * is equal to this TableLayoutConstraints.
1926: *
1927: * @return True, if the entries refer to the same component object.
1928: * False, otherwise.
1929: */
1930:
1931: public boolean equals(Object object) {
1932: boolean equal = false;
1933:
1934: if (object instanceof Component) {
1935: Component component = (Component) object;
1936: equal = (this.component == component);
1937: }
1938:
1939: return equal;
1940: }
1941: }
1942:
1943: }
|