0001: /*
0002: * June 2006: This is the original file except of this paragraph and
0003: * for consistency, the original package name info.clearthought
0004: * was renamed.
0005: */
0006: /*
0007: * ====================================================================
0008: *
0009: * The Clearthought Software License, Version 1.0
0010: *
0011: * Copyright (c) 2001 Daniel Barbalace. All rights reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. The original software may not be altered. However, the classes
0021: * provided may be subclasses as long as the subclasses are not
0022: * packaged in the info.clearthought package or any subpackage of
0023: * info.clearthought.
0024: *
0025: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0026: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0027: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0028: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR, AFFILATED BUSINESSES,
0029: * OR ANYONE ELSE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0030: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0031: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0032: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0033: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0034: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0035: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0036: * SUCH DAMAGE.
0037: * ====================================================================
0038: */
0039:
0040: package org.gui4j.core.swing;
0041:
0042: import java.awt.Component;
0043: import java.awt.Container;
0044: import java.awt.Dimension;
0045: import java.awt.Insets;
0046: import java.util.LinkedList;
0047: import java.util.ListIterator;
0048:
0049: /**
0050: * TableLayout is a layout manager that arranges components in rows and columns
0051: * like a spreadsheet. TableLayout allows each row or column to be a different
0052: * size. A row or column can be given an absolute size in pixels, a percentage
0053: * of the available space, or it can grow and shrink to fill the remaining space
0054: * after other rows and columns have been resized.
0055: *
0056: * <p>Using spreadsheet terminology, a cell is the intersection of a row and
0057: * column. Cells have finite, non-negative sizes measured in pixels. The
0058: * dimensions of a cell depend solely upon the dimensions of its row and column.
0059: * </p>
0060: *
0061: * <p>A component occupies a rectangular group of one or more cells. The
0062: * component can be aligned in four ways within that cell.</p>
0063: *
0064: * <p>A component can be stretched horizontally to fit the cell set (full
0065: * justification), or it can be placed in the center of the cell. The
0066: * component could also be left justified or right justified. Similarly, the
0067: * component can be full, center, top, or bottom justified along the
0068: *
0069: * <pre>
0070: * public static void main (String args[])
0071: * {
0072: * // Create a frame
0073: * Frame frame = new Frame("Example of TableLayout");
0074: * frame.setBounds (100, 100, 300, 300);
0075: * <spc>
0076: * // Create a TableLayout for the frame
0077: * double border = 10;
0078: * double size[][] =
0079: * {{border, 0.10, 20, TableLayout.FILL, 20, 0.20, border}, // Columns
0080: * {border, 0.20, 20, TableLayout.FILL, 20, 0.20, border}}; // Rows
0081: * <spc>
0082: * frame.setLayout (new TableLayout(size));
0083: * <spc>
0084: * // Create some buttons
0085: * String label[] = {"Top", "Bottom", "Left", "Right", "Center", "Overlap"};
0086: * Button button[] = new Button[label.length];
0087: * <spc>
0088: * for (int i = 0; i < label.length; i++)
0089: * button[i] = new Button(label[i]);
0090: * <spc>
0091: * // Add buttons
0092: * frame.add (button[0], "1, 1, 5, 1"); // Top
0093: * frame.add (button[1], "1, 5, 5, 5"); // Bottom
0094: * frame.add (button[2], "1, 3 "); // Left
0095: * frame.add (button[3], "5, 3 "); // Right
0096: * frame.add (button[4], "3, 3, c, c"); // Center
0097: * frame.add (button[5], "3, 3, 3, 5"); // Overlap
0098: * <spc>
0099: * // Allow user to close the window to terminate the program
0100: * frame.addWindowListener
0101: * (new WindowListener()
0102: * {
0103: * public void windowClosing (WindowEvent e)
0104: * {
0105: * System.exit (0);
0106: * }
0107: * <spc>
0108: * public void windowOpened (WindowEvent e) {}
0109: * public void windowClosed (WindowEvent e) {}
0110: * public void windowIconified (WindowEvent e) {}
0111: * public void windowDeiconified (WindowEvent e) {}
0112: * public void windowActivated (WindowEvent e) {}
0113: * public void windowDeactivated (WindowEvent e) {}
0114: * }
0115: * );
0116: * <spc>
0117: * // Show frame
0118: * frame.show();
0119: * }
0120: * </pre>
0121: *
0122: * @version 2.1 4/26/02
0123: * @author Daniel E. Barbalace
0124: */
0125:
0126: public class TableLayout implements java.awt.LayoutManager2,
0127: java.io.Serializable, TableLayoutConstants {
0128:
0129: /*
0130: Note: In this file, a cr refers to either a column or a row. cr[C] always
0131: means column and cr[R] always means row. A cr size is either a column
0132: width or a row Height. TableLayout views columns and rows as being
0133: conceptually symmetric. Therefore, much of the code applies to both
0134: columns and rows, and the use of the cr terminology eliminates redundancy.
0135: Also, for ease of reading, z always indicates a parameter whose value is
0136: either C or R.
0137: */
0138:
0139: /** Default row/column size */
0140: protected static final double defaultSize[][] = { {}, {} };
0141:
0142: /** Indicates a column */
0143: protected static final int C = 0;
0144:
0145: /** Indicates a row */
0146: protected static final int R = 1;
0147:
0148: /** Sizes of crs expressed in absolute and relative terms */
0149: protected double crSpec[][] = { null, null };
0150:
0151: /** Sizes of crs in pixels */
0152: protected int crSize[][] = { null, null };
0153:
0154: /** Offsets of crs in pixels. The left boarder of column n is at
0155: crOffset[C][n] and the right boarder is at cr[C][n + 1] for all
0156: columns including the last one. crOffset[C].length = crSize[C].length + 1 */
0157: protected int crOffset[][] = { null, null };
0158:
0159: /** List of components and their sizes */
0160: protected LinkedList list;
0161:
0162: /** Indicates whether or not the size of the cells are known for the last known
0163: size of the container. If dirty is true or the container has been resized,
0164: the cell sizes must be recalculated using calculateSize. */
0165: protected boolean dirty;
0166:
0167: /** Previous known width of the container */
0168: protected int oldWidth;
0169:
0170: /** Previous known height of the container */
0171: protected int oldHeight;
0172:
0173: /** Horizontal gap between columns */
0174: protected int hGap;
0175:
0176: /** Vertical gap between rows */
0177: protected int vGap;
0178:
0179: //******************************************************************************
0180: //** Constructors ***
0181: //******************************************************************************
0182:
0183: /**
0184: * Constructs an instance of TableLayout. This TableLayout will have one row
0185: * and one column.
0186: */
0187:
0188: public TableLayout() {
0189: this (defaultSize);
0190: }
0191:
0192: /**
0193: * Constructs an instance of TableLayout.
0194: *
0195: * @param size widths of columns and heights of rows in the format,
0196: * {{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}
0197: * If this parameter is invalid, the TableLayout will have
0198: * exactly one row and one column.
0199: */
0200:
0201: public TableLayout(double size[][]) {
0202: // Make sure rows and columns and nothing else is specified
0203: if ((size != null) && (size.length == 2)) {
0204: // Get the rows and columns
0205: double tempCol[] = size[0];
0206: double tempRow[] = size[1];
0207:
0208: // Create new rows and columns
0209: crSpec[C] = new double[tempCol.length];
0210: crSpec[R] = new double[tempRow.length];
0211:
0212: // Copy rows and columns
0213: System
0214: .arraycopy(tempCol, 0, crSpec[C], 0,
0215: crSpec[C].length);
0216: System
0217: .arraycopy(tempRow, 0, crSpec[R], 0,
0218: crSpec[R].length);
0219:
0220: // Make sure rows and columns are valid
0221: for (int counter = 0; counter < crSpec[C].length; counter++)
0222: if ((crSpec[C][counter] < 0.0)
0223: && (crSpec[C][counter] != FILL)
0224: && (crSpec[C][counter] != PREFERRED)
0225: && (crSpec[C][counter] != MINIMUM)) {
0226: crSpec[C][counter] = 0.0;
0227: }
0228:
0229: for (int counter = 0; counter < crSpec[R].length; counter++)
0230: if ((crSpec[R][counter] < 0.0)
0231: && (crSpec[R][counter] != FILL)
0232: && (crSpec[R][counter] != PREFERRED)
0233: && (crSpec[R][counter] != MINIMUM)) {
0234: crSpec[R][counter] = 0.0;
0235: }
0236: } else {
0237: throw new IllegalArgumentException(
0238: "Parameter size should be an array, a[2], where a[0] is the "
0239: + "is an array of column widths and a[1] is an array or row "
0240: + "heights.");
0241: }
0242:
0243: // Create an empty list of components
0244: list = new LinkedList();
0245:
0246: // Indicate that the cell sizes are not known
0247: dirty = true;
0248: }
0249:
0250: //******************************************************************************
0251: //** Get/Set methods ***
0252: //******************************************************************************
0253:
0254: /**
0255: * Gets the constraints of a given component.
0256: *
0257: * @param component desired component
0258: *
0259: * @return If the given component is found, the constraints associated with
0260: * that component. If the given component is null or is not found,
0261: * null is returned.
0262: */
0263:
0264: public TableLayoutConstraints getConstraints(Component component) {
0265: ListIterator iterator = list.listIterator(0);
0266:
0267: while (iterator.hasNext()) {
0268: Entry entry = (Entry) iterator.next();
0269:
0270: if (entry.component == component)
0271: return new TableLayoutConstraints(entry.cr1[C],
0272: entry.cr1[R], entry.cr2[C], entry.cr2[R],
0273: entry.alignment[C], entry.alignment[R]);
0274: }
0275:
0276: return null;
0277: }
0278:
0279: /**
0280: * Sets the constraints of a given component.
0281: *
0282: * @param component desired component. This parameter cannot be null.
0283: * @param constraint new set of constraints. This parameter cannot be null.
0284: */
0285:
0286: public void setConstraints(Component component,
0287: TableLayoutConstraints constraint) {
0288: // Check parameters
0289: if (component == null)
0290: throw new IllegalArgumentException(
0291: "Parameter component cannot be null.");
0292: else if (constraint == null)
0293: throw new IllegalArgumentException(
0294: "Parameter constraint cannot be null.");
0295:
0296: // Find and update constraints for the given component
0297: ListIterator iterator = list.listIterator(0);
0298:
0299: while (iterator.hasNext()) {
0300: Entry entry = (Entry) iterator.next();
0301:
0302: if (entry.component == component)
0303: iterator.set(new Entry(component, constraint));
0304: }
0305: }
0306:
0307: /**
0308: * Adjusts the number and sizes of rows in this layout. After calling this
0309: * method, the caller should request this layout manager to perform the
0310: * layout. This can be done with the following code:
0311: *
0312: * <pre>
0313: * layout.layoutContainer(container);
0314: * container.repaint();
0315: * </pre>
0316: *
0317: * or
0318: *
0319: * <pre>
0320: * window.pack()
0321: * </pre>
0322: *
0323: * If this is not done, the changes in the layout will not be seen until the
0324: * container is resized.
0325: *
0326: * @param column heights of each of the columns
0327: */
0328:
0329: public void setColumn(double column[]) {
0330: setCr(C, column);
0331: }
0332:
0333: /**
0334: * Adjusts the number and sizes of rows in this layout. After calling this
0335: * method, the caller should request this layout manager to perform the
0336: * layout. This can be done with the following code:
0337: *
0338: * <code>
0339: * layout.layoutContainer(container);
0340: * container.repaint();
0341: * </code>
0342: *
0343: * or
0344: *
0345: * <pre>
0346: * window.pack()
0347: * </pre>
0348: *
0349: * If this is not done, the changes in the layout will not be seen until the
0350: * container is resized.
0351: *
0352: * @param row widths of each of the rows. This parameter cannot be null.
0353: */
0354:
0355: public void setRow(double row[]) {
0356: setCr(R, row);
0357: }
0358:
0359: /**
0360: * Sets the sizes of rows or columns for the methods setRow or setColumn.
0361: *
0362: * @param z indicates row or column
0363: * @param size new cr size
0364: */
0365:
0366: protected void setCr(int z, double size[]) {
0367: // Copy crs
0368: crSpec[z] = new double[size.length];
0369: System.arraycopy(size, 0, crSpec[z], 0, crSpec[z].length);
0370:
0371: // Make sure rows are valid
0372: for (int counter = 0; counter < crSpec[z].length; counter++)
0373: if ((crSpec[z][counter] < 0.0)
0374: && (crSpec[z][counter] != FILL)
0375: && (crSpec[z][counter] != PREFERRED)
0376: && (crSpec[z][counter] != MINIMUM)) {
0377: crSpec[z][counter] = 0.0;
0378: }
0379:
0380: // Indicate that the cell sizes are not known
0381: dirty = true;
0382: }
0383:
0384: /**
0385: * Adjusts the width of a single column in this layout. After calling this
0386: * method, the caller should request this layout manager to perform the
0387: * layout. This can be done with the following code:
0388: *
0389: * <code>
0390: * layout.layoutContainer(container);
0391: * container.repaint();
0392: * </code>
0393: *
0394: * or
0395: *
0396: * <pre>
0397: * window.pack()
0398: * </pre>
0399: *
0400: * If this is not done, the changes in the layout will not be seen until the
0401: * container is resized.
0402: *
0403: * @param i zero-based index of column to set. If this parameter is not
0404: * valid, an ArrayOutOfBoundsException will be thrown.
0405: * @param size width of the column. This parameter cannot be null.
0406: */
0407:
0408: public void setColumn(int i, double size) {
0409: setCr(C, i, size);
0410: }
0411:
0412: /**
0413: * Adjusts the height of a single row in this layout. After calling this
0414: * method, the caller should request this layout manager to perform the
0415: * layout. This can be done with the following code:
0416: *
0417: * <code>
0418: * layout.layoutContainer(container);
0419: * container.repaint();
0420: * </code>
0421: *
0422: * or
0423: *
0424: * <pre>
0425: * window.pack()
0426: * </pre>
0427: *
0428: * If this is not done, the changes in the layout will not be seen until the
0429: * container is resized.
0430: *
0431: * @param i zero-based index of row to set. If this parameter is not
0432: * valid, an ArrayOutOfBoundsException will be thrown.
0433: * @param size height of the row. This parameter cannot be null.
0434: */
0435:
0436: public void setRow(int i, double size) {
0437: setCr(R, i, size);
0438: }
0439:
0440: /**
0441: * Sets the sizes of rows or columns for the methods setRow or setColumn.
0442: *
0443: * @param z indicates row or column
0444: * @param i indicates which cr to resize
0445: * @param size new cr size
0446: */
0447:
0448: protected void setCr(int z, int i, double size) {
0449: // Make sure size is valid
0450: if ((size < 0.0) && (size != FILL) && (size != PREFERRED)
0451: && (size != MINIMUM)) {
0452: size = 0.0;
0453: }
0454:
0455: // Copy new size
0456: crSpec[z][i] = size;
0457:
0458: // Indicate that the cell sizes are not known
0459: dirty = true;
0460: }
0461:
0462: /**
0463: * Gets the sizes of columns in this layout.
0464: *
0465: * @return widths of each of the columns
0466: */
0467:
0468: public double[] getColumn() {
0469: // Copy columns
0470: double column[] = new double[crSpec[C].length];
0471: System.arraycopy(crSpec[C], 0, column, 0, column.length);
0472:
0473: return column;
0474: }
0475:
0476: /**
0477: * Gets the height of a single row in this layout.
0478: *
0479: * @return height of the requested row
0480: */
0481:
0482: public double[] getRow() {
0483: // Copy rows
0484: double row[] = new double[crSpec[R].length];
0485: System.arraycopy(crSpec[R], 0, row, 0, row.length);
0486:
0487: return row;
0488: }
0489:
0490: /**
0491: * Gets the width of a single column in this layout.
0492: *
0493: * @param i zero-based index of row to get. If this parameter is not valid,
0494: * an ArrayOutOfBoundsException will be thrown.
0495: *
0496: * @return width of the requested column
0497: */
0498:
0499: public double getColumn(int i) {
0500: return crSpec[C][i];
0501: }
0502:
0503: /**
0504: * Gets the sizes of a row in this layout.
0505: *
0506: * @param i zero-based index of row to get. If this parameter is not valid,
0507: * an ArrayOutOfBoundsException will be thrown.
0508: *
0509: * @return height of each of the requested row
0510: */
0511:
0512: public double getRow(int i) {
0513: return crSpec[R][i];
0514: }
0515:
0516: /**
0517: * Gets the number of columns in this layout.
0518: *
0519: * @return the number of columns
0520: */
0521:
0522: public int getNumColumn() {
0523: return crSpec[C].length;
0524: }
0525:
0526: /**
0527: * Gets the number of rows in this layout.
0528: *
0529: * @return the number of rows
0530: */
0531:
0532: public int getNumRow() {
0533: return crSpec[R].length;
0534: }
0535:
0536: /**
0537: * Gets the horizontal gap between colunns.
0538: *
0539: * @return the horizontal gap in pixels
0540: */
0541:
0542: public int getHGap() {
0543: return hGap;
0544: }
0545:
0546: /**
0547: * Gets the vertical gap between rows.
0548: *
0549: * @return the vertical gap in pixels
0550: */
0551:
0552: public int getVGap() {
0553: return vGap;
0554: }
0555:
0556: /**
0557: * Sets the horizontal gap between colunns.
0558: *
0559: * @param hGap the horizontal gap in pixels
0560: */
0561:
0562: public void setHGap(int hGap) {
0563: if (hGap >= 0)
0564: this .hGap = hGap;
0565: else
0566: throw new IllegalArgumentException(
0567: "Parameter hGap must be non-negative.");
0568: }
0569:
0570: /**
0571: * Sets the vertical gap between rows.
0572: *
0573: * @param vGap the horizontal gap in pixels
0574: */
0575:
0576: public void setVGap(int vGap) {
0577: if (vGap >= 0)
0578: this .vGap = vGap;
0579: else
0580: throw new IllegalArgumentException(
0581: "Parameter vGap must be non-negative.");
0582: }
0583:
0584: //******************************************************************************
0585: //** Insertion/Deletion methods ***
0586: //******************************************************************************
0587:
0588: /**
0589: * Inserts a column in this layout. All components to the right of the
0590: * insertion point are moved right one column. The container will need to
0591: * be laid out after this method returns. See <code>setColumn</code>.
0592: *
0593: * @param i zero-based index at which to insert the column
0594: * @param size size of the column to be inserted
0595: */
0596:
0597: public void insertColumn(int i, double size) {
0598: insertCr(C, i, size);
0599: }
0600:
0601: /**
0602: * Inserts a row in this layout. All components below the insertion point
0603: * are moved down one row. The container will need to be laid out after this
0604: * method returns. See <code>setRow</code>.
0605: *
0606: * @param i zero-based index at which to insert the row
0607: * @param size size of the row to be inserted
0608: */
0609:
0610: public void insertRow(int i, double size) {
0611: insertCr(R, i, size);
0612: }
0613:
0614: /**
0615: * Inserts a cr for the methods insertRow or insertColumn.
0616: *
0617: * @param z indicates row or column
0618: * @param i zero-based index at which to insert the cr
0619: * @param size size of cr being inserted
0620: */
0621:
0622: public void insertCr(int z, int i, double size) {
0623: // Make sure position is valid
0624: if ((i < 0) || (i > crSpec[z].length))
0625: throw new IllegalArgumentException(
0626: "Parameter i is invalid. i = " + i
0627: + ". Valid range is [0, "
0628: + crSpec[z].length + "].");
0629:
0630: // Make sure row size is valid
0631: if ((size < 0.0) && (size != FILL) && (size != PREFERRED)
0632: && (size != MINIMUM)) {
0633: size = 0.0;
0634: }
0635:
0636: // Copy crs
0637: double cr[] = new double[crSpec[z].length + 1];
0638: System.arraycopy(crSpec[z], 0, cr, 0, i);
0639: System.arraycopy(crSpec[z], i, cr, i + 1, crSpec[z].length - i);
0640:
0641: // Insert cr
0642: cr[i] = size;
0643: crSpec[z] = cr;
0644:
0645: // Move all components that are below the new cr
0646: ListIterator iterator = list.listIterator(0);
0647:
0648: while (iterator.hasNext()) {
0649: // Get next entry
0650: Entry entry = (Entry) iterator.next();
0651:
0652: // Is the first cr below the new cr
0653: if (entry.cr1[z] >= i)
0654: // Move first cr
0655: entry.cr1[z]++;
0656:
0657: // Is the second cr below the new cr
0658: if (entry.cr2[z] >= i)
0659: // Move second cr
0660: entry.cr2[z]++;
0661: }
0662:
0663: // Indicate that the cell sizes are not known
0664: dirty = true;
0665: }
0666:
0667: /**
0668: * Deletes a column in this layout. All components to the right of the
0669: * deletion point are moved left one column. The container will need to
0670: * be laid out after this method returns. See <code>setColumn</code>.
0671: *
0672: * @param i zero-based index of column to delete
0673: */
0674:
0675: public void deleteColumn(int i) {
0676: deleteCr(C, i);
0677: }
0678:
0679: /**
0680: * Deletes a row in this layout. All components below the deletion point are
0681: * moved up one row. The container will need to be laid out after this method
0682: * returns. See <code>setRow</code>. There must be at least two rows in order
0683: * to delete a row.
0684: *
0685: * @param i zero-based index of row to delete
0686: */
0687:
0688: public void deleteRow(int i) {
0689: deleteCr(R, i);
0690: }
0691:
0692: /**
0693: * Deletes a cr for the methods deleteRow or deleteColumn.
0694: *
0695: * @param z indicates row or column
0696: * @param i zero-based index of cr to delete
0697: */
0698:
0699: protected void deleteCr(int z, int i) {
0700: // Make sure position is valid
0701: if ((i < 0) || (i >= crSpec[z].length))
0702: throw new IllegalArgumentException(
0703: "Parameter i is invalid. i = " + i
0704: + ". Valid range is [0, "
0705: + (crSpec[z].length - 1) + "].");
0706:
0707: // Copy rows
0708: double cr[] = new double[crSpec[z].length - 1];
0709: System.arraycopy(crSpec[z], 0, cr, 0, i);
0710: System.arraycopy(crSpec[z], i + 1, cr, i, crSpec[z].length - i
0711: - 1);
0712:
0713: // Delete row
0714: crSpec[z] = cr;
0715:
0716: // Move all components that are to below the row deleted
0717: ListIterator iterator = list.listIterator(0);
0718:
0719: while (iterator.hasNext()) {
0720: // Get next entry
0721: Entry entry = (Entry) iterator.next();
0722:
0723: // Is the first row below the new row
0724: if (entry.cr1[z] > i)
0725: // Move first row
0726: entry.cr1[z]--;
0727:
0728: // Is the second row below the new row
0729: if (entry.cr2[z] > i)
0730: // Move second row
0731: entry.cr2[z]--;
0732: }
0733:
0734: // Indicate that the cell sizes are not known
0735: dirty = true;
0736: }
0737:
0738: //******************************************************************************
0739: //** Misc methods ***
0740: //******************************************************************************
0741:
0742: /**
0743: * Converts this TableLayout to a string.
0744: *
0745: * @return a string representing the columns and row sizes in the form
0746: * "{{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}"
0747: */
0748:
0749: public String toString() {
0750: int counter;
0751:
0752: String value = "TableLayout {{";
0753:
0754: if (crSpec[C].length > 0) {
0755: for (counter = 0; counter < crSpec[C].length - 1; counter++)
0756: value += crSpec[C][counter] + ", ";
0757:
0758: value += crSpec[C][crSpec[C].length - 1] + "}, {";
0759: } else
0760: value += "}, {";
0761:
0762: if (crSpec[R].length > 0) {
0763: for (counter = 0; counter < crSpec[R].length - 1; counter++)
0764: value += crSpec[R][counter] + ", ";
0765:
0766: value += crSpec[R][crSpec[R].length - 1] + "}}";
0767: } else
0768: value += "}}";
0769:
0770: return value;
0771: }
0772:
0773: /**
0774: * Determines whether or not there are any components with invalid constraints.
0775: * An invalid constraint is one that references a non-existing row or column.
0776: * For example, on a table with five rows, row -1 and row 5 are both invalid.
0777: * Valid rows are 0 through 4, inclusively.
0778: *
0779: * @return an array of zero or more Component instances
0780: */
0781:
0782: public Component[] getInvalidEntry() {
0783: LinkedList listInvalid = new LinkedList();
0784: ListIterator iterator = list.listIterator(0);
0785:
0786: while (iterator.hasNext()) {
0787: Entry entry = (Entry) iterator.next();
0788:
0789: if ((entry.cr1[R] < 0) || (entry.cr1[C] < 0)
0790: || (entry.cr2[R] >= crSpec[R].length)
0791: || (entry.cr2[C] >= crSpec[C].length)) {
0792: listInvalid.add(entry.component);
0793: }
0794: }
0795:
0796: return (Component[]) listInvalid
0797: .toArray(new Component[listInvalid.size()]);
0798: }
0799:
0800: /**
0801: * Gets a list of overlapping components. Two components overlap if they cover
0802: * at least one common cell.
0803: *
0804: * @return a list of zero or more Component instances
0805: */
0806:
0807: public Component[] getOverlappingEntry() {
0808: LinkedList listOverlapping = new LinkedList();
0809: int numEntry = list.size();
0810: Entry entry[] = (Entry[]) list.toArray(new Entry[numEntry]);
0811:
0812: for (int knowUnique = 1; knowUnique < numEntry; knowUnique++)
0813: for (int checking = knowUnique - 1; checking >= 0; checking--)
0814: if (((entry[checking].cr1[C] >= entry[knowUnique].cr1[C])
0815: && (entry[checking].cr1[C] <= entry[knowUnique].cr2[C])
0816: && (entry[checking].cr1[R] >= entry[knowUnique].cr1[R]) && (entry[checking].cr1[R] <= entry[knowUnique].cr2[R]))
0817: || ((entry[checking].cr2[C] >= entry[knowUnique].cr1[C])
0818: && (entry[checking].cr2[C] <= entry[knowUnique].cr2[C])
0819: && (entry[checking].cr2[R] >= entry[knowUnique].cr1[R]) && (entry[checking].cr2[R] <= entry[knowUnique].cr2[R]))) {
0820: listOverlapping.add(entry[checking].component);
0821: }
0822:
0823: return (Component[]) listOverlapping
0824: .toArray(new Component[listOverlapping.size()]);
0825: }
0826:
0827: //******************************************************************************
0828: //** Calculation methods ***
0829: //******************************************************************************
0830:
0831: /**
0832: * Calculates the sizes of the rows and columns based on the absolute and
0833: * relative sizes specified in <code>crSpec[R]</code> and <code>crSpec[C]</code>
0834: * and the size of the container. The result is stored in <code>crSize[R]</code>
0835: * and <code>crSize[C]</code>.
0836: *
0837: * @param container container using this TableLayout
0838: */
0839:
0840: protected void calculateSize(Container container) {
0841: // Get the container's insets
0842: Insets inset = container.getInsets();
0843:
0844: // Get the size of the container's available space
0845: Dimension d = container.getSize();
0846: int availableWidth = d.width - inset.left - inset.right;
0847: int availableHeight = d.height - inset.top - inset.bottom;
0848:
0849: // Compensate for horiztonal and vertical gaps
0850: if (crSpec[C].length > 0)
0851: availableWidth -= hGap * (crSpec[C].length - 1);
0852:
0853: if (crSpec[R].length > 0)
0854: availableHeight -= vGap * (crSpec[R].length - 1);
0855:
0856: // Create array to hold actual sizes in pixels
0857: crSize[C] = new int[crSpec[C].length];
0858: crSize[R] = new int[crSpec[R].length];
0859:
0860: // Assign absolute sizes
0861: availableWidth = assignAbsoluteSize(C, availableWidth);
0862: availableHeight = assignAbsoluteSize(R, availableHeight);
0863:
0864: // Assign preferred and minimum sizes
0865: availableWidth = assignPrefMinSize(C, availableWidth, MINIMUM);
0866: availableWidth = assignPrefMinSize(C, availableWidth, PREFERRED);
0867: availableHeight = assignPrefMinSize(R, availableHeight, MINIMUM);
0868: availableHeight = assignPrefMinSize(R, availableHeight,
0869: PREFERRED);
0870:
0871: // Assign relative sizes
0872: availableWidth = assignRelativeSize(C, availableWidth);
0873: availableHeight = assignRelativeSize(R, availableHeight);
0874:
0875: // Assign fill sizes
0876: assignFillSize(C, availableWidth);
0877: assignFillSize(R, availableHeight);
0878:
0879: // Calculate cr offsets for effeciency
0880: calculateOffset(C, inset);
0881: calculateOffset(R, inset);
0882:
0883: // Indicate that the size of the cells are known for the container's
0884: // current size
0885: dirty = false;
0886: oldWidth = d.width;
0887: oldHeight = d.height;
0888: }
0889:
0890: /**
0891: * Assigns absolute sizes.
0892: *
0893: * @param z indicates row or column
0894: * @param availableSize amount of space available in the container
0895: *
0896: * @return the amount of space available after absolute crs have been assigned
0897: * sizes
0898: */
0899:
0900: protected int assignAbsoluteSize(int z, int availableSize) {
0901: int numCr = crSpec[z].length;
0902:
0903: for (int counter = 0; counter < numCr; counter++)
0904: if ((crSpec[z][counter] >= 1.0)
0905: || (crSpec[z][counter] == 0.0)) {
0906: crSize[z][counter] = (int) (crSpec[z][counter] + 0.5);
0907: availableSize -= crSize[z][counter];
0908: }
0909:
0910: return availableSize;
0911: }
0912:
0913: /**
0914: * Assigns relative sizes.
0915: *
0916: * @param z indicates row or column
0917: * @param availableSize amount of space available in the container
0918: *
0919: * @return the amount of space available after relative crs have been assigned
0920: * sizes
0921: */
0922:
0923: protected int assignRelativeSize(int z, int availableSize) {
0924: int relativeSize = (availableSize < 0) ? 0 : availableSize;
0925: int numCr = crSpec[z].length;
0926:
0927: for (int counter = 0; counter < numCr; counter++)
0928: if ((crSpec[z][counter] > 0.0)
0929: && (crSpec[z][counter] < 1.0)) {
0930: crSize[z][counter] = (int) (crSpec[z][counter]
0931: * relativeSize + 0.5);
0932:
0933: availableSize -= crSize[z][counter];
0934: }
0935:
0936: return availableSize;
0937: }
0938:
0939: /**
0940: * Assigns FILL sizes.
0941: *
0942: * @param z indicates row or column
0943: * @param availableSize amount of space available in the container
0944: */
0945:
0946: protected void assignFillSize(int z, int availableSize) {
0947: // Skip if there is no more space to allocate
0948: if (availableSize <= 0)
0949: return;
0950:
0951: // Count the number of "fill" cells
0952: int numFillSize = 0;
0953: int numCr = crSpec[z].length;
0954:
0955: for (int counter = 0; counter < numCr; counter++)
0956: if (crSpec[z][counter] == FILL)
0957: numFillSize++;
0958:
0959: // If numFillSize is zero, the if statement below will always evaluate to
0960: // false and the division will not occur.
0961:
0962: // If there are more than one "fill" cell, slack may occur due to rounding
0963: // errors
0964: int slackSize = availableSize;
0965:
0966: // Assign "fill" cells equal amounts of the remaining space
0967: for (int counter = 0; counter < numCr; counter++)
0968: if (crSpec[z][counter] == FILL) {
0969: crSize[z][counter] = availableSize / numFillSize;
0970: slackSize -= crSize[z][counter];
0971: }
0972:
0973: // Assign one pixel of slack to each FILL cr, starting at the last one,
0974: // until all slack has been consumed
0975: for (int counter = numCr - 1; (counter >= 0) && (slackSize > 0); counter--) {
0976: if (crSpec[z][counter] == FILL) {
0977: crSize[z][counter]++;
0978: slackSize--;
0979: }
0980: }
0981: }
0982:
0983: /**
0984: * Calculates the offset of each cr.
0985: *
0986: * @param z indicates row or column
0987: * @param inset
0988: */
0989:
0990: protected void calculateOffset(int z, Insets inset) {
0991: int numCr = crSpec[z].length;
0992:
0993: crOffset[z] = new int[numCr + 1];
0994: crOffset[z][0] = (z == C) ? inset.left : inset.top;
0995:
0996: for (int counter = 0; counter < numCr; counter++)
0997: crOffset[z][counter + 1] = crOffset[z][counter]
0998: + crSize[z][counter];
0999: }
1000:
1001: /**
1002: * Assigned widths to preferred and minimum size columns and rows. This
1003: * reduces the available width and height. Minimum widths/heights must be
1004: * calculated first because they affect preferred widths/heights, but not vice
1005: * versa. The end result is that any component contained wholly or partly in
1006: * a column/row of minimum/preferred width will get at least its
1007: * minimum/preferred width, respectively.
1008: *
1009: * @param z indicates row or column
1010: * @param availableSize amount of space available in the container
1011: * @param typeOfSize indicates preferred or minimum
1012: *
1013: * @return the amount of space available after absolute crs have been assigned
1014: * sizes
1015: */
1016:
1017: protected int assignPrefMinSize(int z, int availableSize,
1018: double typeOfSize) {
1019: // Get variables referring to columns or rows (crs)
1020: int numCr = crSpec[z].length;
1021:
1022: // Address every cr
1023: for (int counter = 0; counter < numCr; counter++)
1024: // Is the current cr a preferred/minimum (based on typeOfSize) size
1025: if (crSpec[z][counter] == typeOfSize) {
1026: // Assume a maximum width of zero
1027: int maxSize = 0;
1028:
1029: // Find maximum preferred/min width of all components completely
1030: // or partially contained within this cr
1031: ListIterator iterator = list.listIterator(0);
1032:
1033: nextComponent: while (iterator.hasNext()) {
1034: Entry entry = (Entry) iterator.next();
1035:
1036: // Skip invalid entries
1037: if ((entry.cr1[z] < 0) || (entry.cr2[z] >= numCr))
1038: continue nextComponent;
1039:
1040: // Find the maximum desired size of this cr based on all crs
1041: // the current component occupies
1042: if ((entry.cr1[z] <= counter)
1043: && (entry.cr2[z] >= counter)) {
1044: // Setup size and number of adjustable crs
1045: Dimension p = (typeOfSize == PREFERRED) ? entry.component
1046: .getPreferredSize()
1047: : entry.component.getMinimumSize();
1048:
1049: int size = (p == null) ? 0
1050: : ((z == C) ? p.width : p.height);
1051: int numAdjustable = 0;
1052:
1053: // Calculate for preferred size
1054: if (typeOfSize == PREFERRED)
1055: // Consider all crs this component occupies
1056: for (int entryCr = entry.cr1[z]; entryCr <= entry.cr2[z]; entryCr++) {
1057: // Subtract absolute, relative, and minumum cr
1058: // sizes, which have already been calculated
1059: if ((crSpec[z][entryCr] >= 0.0)
1060: || (crSpec[z][entryCr] == MINIMUM)) {
1061: size -= crSize[z][entryCr];
1062: }
1063: // Count preferred/min width columns
1064: else if (crSpec[z][entryCr] == PREFERRED)
1065: numAdjustable++;
1066: // Skip any component that occupies a fill cr
1067: // because the fill should fulfill the size
1068: // requirements
1069: else if (crSpec[z][entryCr] == FILL)
1070: continue nextComponent;
1071: }
1072: // Calculate for minimum size
1073: else
1074: // Consider all crs this component occupies
1075: for (int entryCr = entry.cr1[z]; entryCr <= entry.cr2[z]; entryCr++) {
1076: // Subtract absolute and relative cr sizes, which
1077: // have already been calculated
1078: if (crSpec[z][entryCr] >= 0.0)
1079: size -= crSize[z][entryCr];
1080: // Count preferred/min width columns
1081: else if ((crSpec[z][entryCr] == PREFERRED)
1082: || (crSpec[z][entryCr] == MINIMUM)) {
1083: numAdjustable++;
1084: }
1085: // Skip any component that occupies a fill cr
1086: // because the fill should fulfill the size
1087: // requirements
1088: else if (crSpec[z][entryCr] == FILL)
1089: continue nextComponent;
1090: }
1091:
1092: // Divide the size evenly among the adjustable crs
1093: size = (int) Math.ceil(size
1094: / (double) numAdjustable);
1095:
1096: // Take the maximumn size
1097: if (maxSize < size)
1098: maxSize = size;
1099: }
1100: }
1101:
1102: // Assign preferred size
1103: crSize[z][counter] = maxSize;
1104:
1105: // Reduce available size
1106: availableSize -= maxSize;
1107: }
1108:
1109: return availableSize;
1110: }
1111:
1112: //******************************************************************************
1113: //** java.awt.event.LayoutManager methods ***
1114: //******************************************************************************
1115:
1116: /**
1117: * To lay out the specified container using this layout. This method reshapes
1118: * the components in the specified target container in order to satisfy the
1119: * constraints of all components.
1120: *
1121: * <p>User code should not have to call this method directly.</p>
1122: *
1123: * @param container container being served by this layout manager
1124: */
1125:
1126: public void layoutContainer(Container container) {
1127: // Calculate sizes if container has changed size or components were added
1128: Dimension d = container.getSize();
1129:
1130: if (dirty || (d.width != oldWidth) || (d.height != oldHeight))
1131: calculateSize(container);
1132:
1133: // Get components
1134: Component component[] = container.getComponents();
1135:
1136: // Layout components
1137: for (int counter = 0; counter < component.length; counter++) {
1138: try {
1139: // Get the entry for the next component
1140: ListIterator iterator = list.listIterator(0);
1141: Entry entry = null;
1142:
1143: while (iterator.hasNext()) {
1144: entry = (Entry) iterator.next();
1145:
1146: if (entry.component == component[counter])
1147: break;
1148: else
1149: entry = null;
1150: }
1151:
1152: // Skip any components that have not been place in a specific cell,
1153: // setting the skip component's bounds to zero
1154: if (entry == null) {
1155: component[counter].setBounds(0, 0, 0, 0);
1156: continue;
1157: }
1158:
1159: // The following block of code has been optimized so that the
1160: // preferred size of the component is only obtained if it is
1161: // needed. There are components in which the getPreferredSize
1162: // method is extremely expensive, such as data driven controls
1163: // with a large amount of data.
1164:
1165: // Get the preferred size of the component
1166: int preferredWidth = 0;
1167: int preferredHeight = 0;
1168:
1169: if ((entry.alignment[C] != FULL)
1170: || (entry.alignment[R] != FULL)) {
1171: Dimension preferredSize = component[counter]
1172: .getPreferredSize();
1173:
1174: preferredWidth = preferredSize.width;
1175: preferredHeight = preferredSize.height;
1176: }
1177:
1178: // Calculate the coordinates and size of the component
1179: int value[] = calculateSizeAndOffset(entry,
1180: preferredWidth, true);
1181: int x = value[0];
1182: int w = value[1];
1183: value = calculateSizeAndOffset(entry, preferredHeight,
1184: false);
1185: int y = value[0];
1186: int h = value[1];
1187:
1188: // Move and resize component
1189: component[counter].setBounds(x, y, w, h);
1190: } catch (Exception error) {
1191: // If any error occurs, set the bounds of this component to zero
1192: // and continue
1193: component[counter].setBounds(0, 0, 0, 0);
1194: continue;
1195: }
1196: }
1197: }
1198:
1199: /**
1200: * Calculates the vertical/horizontal offset and size of a component.
1201: *
1202: * @param entry entry containing component and contraints
1203: * @param preferredSize previously calculated preferred width/height of
1204: * component
1205: * @param isColumn if true, this method is being called to calculate
1206: * the offset/size of a column. if false,... of a row.
1207: *
1208: * @return an array, a, of two integers such that a[0] is the offset and
1209: * a[1] is the size
1210: */
1211:
1212: protected int[] calculateSizeAndOffset(Entry entry,
1213: int preferredSize, boolean isColumn) {
1214: // Get references to cr properties
1215: int lCrOffset[] = isColumn ? this .crOffset[C]
1216: : this .crOffset[R];
1217: int entryAlignment = isColumn ? entry.alignment[C]
1218: : entry.alignment[R];
1219:
1220: // Determine cell set size
1221: int cellSetSize = isColumn ? lCrOffset[entry.cr2[C] + 1]
1222: - lCrOffset[entry.cr1[C]] : lCrOffset[entry.cr2[R] + 1]
1223: - lCrOffset[entry.cr1[R]];
1224:
1225: // Determine the size of the component
1226: int size;
1227:
1228: if ((entryAlignment == FULL) || (cellSetSize < preferredSize))
1229: size = cellSetSize;
1230: else
1231: size = preferredSize;
1232:
1233: // Determine offset
1234: int offset;
1235:
1236: switch (entryAlignment) {
1237: case LEFT: // Align left/top side along left edge of cell
1238: offset = lCrOffset[isColumn ? entry.cr1[C] : entry.cr1[R]];
1239: break;
1240:
1241: case RIGHT: // Align right/bottom side along right edge of cell
1242: offset = lCrOffset[(isColumn ? entry.cr2[C] : entry.cr2[R]) + 1]
1243: - size;
1244: break;
1245:
1246: case CENTER: // Center justify component
1247: offset = lCrOffset[isColumn ? entry.cr1[C] : entry.cr1[R]]
1248: + ((cellSetSize - size) >> 1);
1249: break;
1250:
1251: case FULL: // Align left/top side along left/top edge of cell
1252: offset = lCrOffset[isColumn ? entry.cr1[C] : entry.cr1[R]];
1253: break;
1254:
1255: default: // This is a never should happen case, but just in case
1256: offset = 0;
1257: }
1258:
1259: // Compensate for gaps
1260: if (isColumn) {
1261: offset += hGap * entry.cr1[C];
1262: size += hGap * (entry.cr2[C] - entry.cr1[C]);
1263: } else {
1264: offset += vGap * entry.cr1[R];
1265: size += hGap * (entry.cr2[R] - entry.cr1[R]);
1266: }
1267:
1268: // Package return values
1269: int value[] = { offset, size };
1270: return value;
1271: }
1272:
1273: /**
1274: * Determines the preferred size of the container argument using this layout.
1275: * The preferred size is the smallest size that, if used for the container's
1276: * size, will ensure that all components are at least as large as their
1277: * preferred size. This method cannot guarantee that all components will be
1278: * their preferred size. For example, if component A and component B are each
1279: * allocate half of the container's width and component A wants to be 10 pixels
1280: * wide while component B wants to be 100 pixels wide, they cannot both be
1281: * accommodated. Since in general components rather be larger than their
1282: * preferred size instead of smaller, component B's request will be fulfilled.
1283: * The preferred size of the container would be 200 pixels.
1284: *
1285: * @param container container being served by this layout manager
1286: *
1287: * @return a dimension indicating the container's preferred size
1288: */
1289:
1290: public Dimension preferredLayoutSize(Container container) {
1291: return calculateLayoutSize(container, PREFERRED);
1292: }
1293:
1294: /**
1295: * Determines the minimum size of the container argument using this layout.
1296: * The minimum size is the smallest size that, if used for the container's
1297: * size, will ensure that all components are at least as large as their
1298: * minimum size. This method cannot guarantee that all components will be
1299: * their minimum size. For example, if component A and component B are each
1300: * allocate half of the container's width and component A wants to be 10 pixels
1301: * wide while component B wants to be 100 pixels wide, they cannot both be
1302: * accommodated. Since in general components rather be larger than their
1303: * minimum size instead of smaller, component B's request will be fulfilled.
1304: * The minimum size of the container would be 200 pixels.
1305: *
1306: * @param container container being served by this layout manager
1307: *
1308: * @return a dimension indicating the container's minimum size
1309: */
1310:
1311: public Dimension minimumLayoutSize(Container container) {
1312: return calculateLayoutSize(container, MINIMUM);
1313: }
1314:
1315: /**
1316: * Calculates the preferred or minimum size for the methods preferredLayoutSize
1317: * and minimumLayoutSize.
1318: *
1319: * @param container container whose size is being calculated
1320: * @param typeOfSize indicates preferred or minimum
1321: *
1322: * @return a dimension indicating the container's preferred or minimum size
1323: */
1324:
1325: protected Dimension calculateLayoutSize(Container container,
1326: double typeOfSize) {
1327: // Get preferred/minimum sizes
1328: Entry entryList[] = (Entry[]) list.toArray(new Entry[list
1329: .size()]);
1330: int numEntry = entryList.length;
1331: Dimension prefMinSize[] = new Dimension[numEntry];
1332:
1333: for (int i = 0; i < numEntry; i++)
1334: prefMinSize[i] = (typeOfSize == PREFERRED) ? entryList[i].component
1335: .getPreferredSize()
1336: : entryList[i].component.getMinimumSize();
1337:
1338: // Calculate sizes
1339: int width = calculateLayoutSize(container, C, typeOfSize,
1340: entryList, prefMinSize);
1341:
1342: int height = calculateLayoutSize(container, R, typeOfSize,
1343: entryList, prefMinSize);
1344:
1345: // Compensate for container's insets
1346: Insets inset = container.getInsets();
1347: width += inset.left + inset.right;
1348: height += inset.top + inset.bottom;
1349:
1350: return new Dimension(width, height);
1351: }
1352:
1353: /**
1354: * Calculates the preferred or minimum size for the method
1355: * calculateLayoutSize(Container container, double typeOfSize). This method
1356: * is passed the preferred/minimum sizes of the components so that the
1357: * potentially expensive methods getPreferredSize()/getMinimumSize() are not
1358: * called twice for the same component.
1359: *
1360: * @param container container whose size is being calculated
1361: * @param z
1362: * @param typeOfSize indicates preferred or minimum
1363: * @param entryList list of Entry objects
1364: * @param prefMinSize list of preferred or minimum sizes
1365: *
1366: * @return a dimension indicating the container's preferred or minimum size
1367: */
1368:
1369: protected int calculateLayoutSize(Container container, int z,
1370: double typeOfSize, Entry entryList[],
1371: Dimension prefMinSize[]) {
1372: Dimension size; // Preferred/minimum size of current component
1373: int scaledSize = 0; // Preferred/minimum size of scaled components
1374: int temp; // Temporary variable used to compare sizes
1375: int counter; // Counting variable
1376:
1377: // Get number of crs
1378: int numCr = crSpec[z].length;
1379:
1380: // Determine percentage of space allocated to fill components. This is
1381: // one minus the sum of all scalable components.
1382: double fillSizeRatio = 1.0;
1383: int numFillSize = 0;
1384:
1385: for (counter = 0; counter < numCr; counter++)
1386: if ((crSpec[z][counter] > 0.0)
1387: && (crSpec[z][counter] < 1.0))
1388: fillSizeRatio -= crSpec[z][counter];
1389: else if (crSpec[z][counter] == FILL)
1390: numFillSize++;
1391:
1392: // Adjust fill ratios to reflect number of fill rows/columns
1393: if (numFillSize > 1)
1394: fillSizeRatio /= numFillSize;
1395:
1396: // Cap fill ratio bottoms to 0.0
1397: if (fillSizeRatio < 0.0)
1398: fillSizeRatio = 0.0;
1399:
1400: // Create array to hold actual sizes in pixels
1401: crSize[z] = new int[numCr];
1402:
1403: // Calculate preferred/minimum cr sizes
1404: assignPrefMinSize(z, 0, typeOfSize);
1405:
1406: int crPrefMin[] = new int[numCr];
1407:
1408: for (counter = 0; counter < numCr; counter++)
1409: if ((crSpec[z][counter] == PREFERRED)
1410: || (crSpec[z][counter] == MINIMUM)) {
1411: crPrefMin[counter] = crSize[z][counter];
1412: }
1413:
1414: // Find maximum preferred/minimum size of all scaled components
1415: int numColumn = crSpec[C].length;
1416: int numRow = crSpec[R].length;
1417: int numEntry = entryList.length;
1418:
1419: for (int entryCounter = 0; entryCounter < numEntry; entryCounter++) {
1420: // Get next entry
1421: Entry entry = entryList[entryCounter];
1422:
1423: // Make sure entry is in valid rows and columns
1424: if ((entry.cr1[C] < 0) || (entry.cr1[C] >= numColumn)
1425: || (entry.cr2[C] >= numColumn)
1426: || (entry.cr1[R] < 0) || (entry.cr1[R] >= numRow)
1427: || (entry.cr2[R] >= numRow)) {
1428: // Skip the bad component
1429: continue;
1430: }
1431:
1432: // Get preferred/minimum size of current component
1433: size = prefMinSize[entryCounter];
1434:
1435: //----------------------------------------------------------------------
1436:
1437: // Calculate portion of component that is not absolutely sized
1438: int scalableSize = (z == C) ? size.width : size.height;
1439:
1440: for (counter = entry.cr1[z]; counter <= entry.cr2[z]; counter++)
1441: if (crSpec[z][counter] >= 1.0)
1442: scalableSize -= crSpec[z][counter];
1443: else if ((crSpec[z][counter] == PREFERRED)
1444: || (crSpec[z][counter] == MINIMUM)) {
1445: scalableSize -= crPrefMin[counter];
1446: }
1447:
1448: //----------------------------------------------------------------------
1449:
1450: // Determine total percentage of scalable space that the component
1451: // occupies by adding the relative columns and the fill columns
1452: double relativeSize = 0.0;
1453:
1454: for (counter = entry.cr1[z]; counter <= entry.cr2[z]; counter++) {
1455: // Cr is scaled
1456: if ((crSpec[z][counter] > 0.0)
1457: && (crSpec[z][counter] < 1.0))
1458: // Add scaled size to relativeWidth
1459: relativeSize += crSpec[z][counter];
1460: // Cr is fill
1461: else if ((crSpec[z][counter] == FILL)
1462: && (fillSizeRatio != 0.0))
1463: // Add fill size to relativeWidth
1464: relativeSize += fillSizeRatio;
1465: }
1466:
1467: // Determine the total scaled size as estimated by this component
1468: if (relativeSize == 0)
1469: temp = 0;
1470: else
1471: temp = (int) (scalableSize / relativeSize + 0.5);
1472:
1473: //----------------------------------------------------------------------
1474:
1475: // If the container needs to be bigger, make it so
1476: if (scaledSize < temp)
1477: scaledSize = temp;
1478: }
1479:
1480: // totalSize is the scaledSize plus the sum of all absolute sizes and all
1481: // preferred sizes
1482: int totalSize = scaledSize;
1483:
1484: for (counter = 0; counter < numCr; counter++)
1485: // Is the current cr an absolute size
1486: if (crSpec[z][counter] >= 1.0)
1487: totalSize += (int) (crSpec[z][counter] + 0.5);
1488: // Is the current cr a preferred/minimum size
1489: else if ((crSpec[z][counter] == PREFERRED)
1490: || (crSpec[z][counter] == MINIMUM)) {
1491: // Add preferred/minimum width
1492: totalSize += crPrefMin[counter];
1493: }
1494:
1495: // Compensate for horizontal and vertical gap
1496: if (numCr > 0)
1497: totalSize += ((z == C) ? hGap : vGap) * (numCr - 1);
1498:
1499: return totalSize;
1500: }
1501:
1502: /**
1503: * Adds the specified component with the specified name to the layout.
1504: *
1505: * @param name indicates entry's position and anchor
1506: * @param component component to add
1507: */
1508:
1509: public void addLayoutComponent(String name, Component component) {
1510: addLayoutComponent(component, name);
1511: }
1512:
1513: //******************************************************************************
1514: //** java.awt.event.LayoutManager2 methods ***
1515: //******************************************************************************
1516:
1517: /**
1518: * Adds the specified component with the specified name to the layout.
1519: *
1520: * @param component component to add
1521: * @param constraint indicates entry's position and alignment
1522: */
1523:
1524: public void addLayoutComponent(Component component,
1525: Object constraint) {
1526: if (constraint instanceof String) {
1527: // Create an entry to associate component with its constraints
1528: constraint = new TableLayoutConstraints((String) constraint);
1529:
1530: // Add component and constraints to the list
1531: list.add(new Entry(component,
1532: (TableLayoutConstraints) constraint));
1533:
1534: // Indicate that the cell sizes are not known
1535: dirty = true;
1536: } else if (constraint instanceof TableLayoutConstraints) {
1537: // Add component and constraints to the list
1538: list.add(new Entry(component,
1539: (TableLayoutConstraints) constraint));
1540:
1541: // Indicate that the cell sizes are not known
1542: dirty = true;
1543: } else if (constraint == null)
1544: throw new IllegalArgumentException(
1545: "No constraint for the component");
1546: else
1547: throw new IllegalArgumentException(
1548: "Cannot accept a constraint of class "
1549: + constraint.getClass());
1550: }
1551:
1552: /**
1553: * Removes the specified component from the layout.
1554: *
1555: * @param component component being removed
1556: */
1557:
1558: public void removeLayoutComponent(Component component) {
1559: // Remove the component
1560: ListIterator iterator = list.listIterator(0);
1561:
1562: while (iterator.hasNext()) {
1563: Entry entry = (Entry) iterator.next();
1564:
1565: if (entry.component == component)
1566: iterator.remove();
1567: }
1568:
1569: // Indicate that the cell sizes are not known since
1570: dirty = true;
1571: }
1572:
1573: /**
1574: * Returns the maximum dimensions for this layout given the components in the
1575: * specified target container.
1576: *
1577: * @param target the component which needs to be laid out
1578: *
1579: * @return unconditionally, a Dimension of Integer.MAX_VALUE by
1580: * Integer.MAX_VALUE since TableLayout does not limit the
1581: * maximum size of a container
1582: */
1583:
1584: public Dimension maximumLayoutSize(Container target) {
1585: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1586: }
1587:
1588: /**
1589: * Returns the alignment along the x axis. This specifies how the component
1590: * would like to be aligned relative to other components. The value should be
1591: * a number between 0 and 1 where 0 represents alignment along the origin, 1 is
1592: * aligned the furthest away from the origin, 0.5 is centered, etc.
1593: * @param parent
1594: * @return unconditionally, 0.5
1595: */
1596:
1597: public float getLayoutAlignmentX(Container parent) {
1598: return 0.5f;
1599: }
1600:
1601: /**
1602: * Returns the alignment along the y axis. This specifies how the component
1603: * would like to be aligned relative to other components. The value should be
1604: * a number between 0 and 1 where 0 represents alignment along the origin, 1 is
1605: * aligned the furthest away from the origin, 0.5 is centered, etc.
1606: * @param parent
1607: * @return unconditionally, 0.5
1608: */
1609:
1610: public float getLayoutAlignmentY(Container parent) {
1611: return 0.5f;
1612: }
1613:
1614: /**
1615: * Invalidates the layout, indicating that if the layout manager has cached
1616: * information it should be discarded.
1617: * @param target
1618: */
1619:
1620: public void invalidateLayout(Container target) {
1621: dirty = true;
1622: }
1623:
1624: //******************************************************************************
1625: //*** Inner Class ***
1626: //******************************************************************************
1627:
1628: // The following inner class is used to bind components to their constraints
1629: protected static class Entry implements Cloneable {
1630: /** Component bound by the constraints */
1631: protected Component component;
1632:
1633: /** Cell in which the upper-left corner of the component lies */
1634: protected int cr1[];
1635:
1636: /** Cell in which the lower-right corner of the component lies */
1637: protected int cr2[];
1638:
1639: /** Horizontal and vertical alignment */
1640: protected int alignment[];
1641:
1642: /**
1643: * Constructs an Entry that binds a component to a set of constraints.
1644: *
1645: * @param component component being bound
1646: * @param constraint constraints being applied
1647: */
1648:
1649: protected Entry(Component component,
1650: TableLayoutConstraints constraint) {
1651: int lCr1[] = { constraint.col1, constraint.row1 };
1652: int lCr2[] = { constraint.col2, constraint.row2 };
1653: int lAlignment[] = { constraint.hAlign, constraint.vAlign };
1654:
1655: this .cr1 = lCr1;
1656: this .cr2 = lCr2;
1657: this .alignment = lAlignment;
1658: this .component = component;
1659: }
1660:
1661: /**
1662: * Gets the string representation of this Entry.
1663: *
1664: * @return a string in the form
1665: * "(col1, row1, col2, row2, vAlign, hAlign) component"
1666: */
1667:
1668: public String toString() {
1669: TableLayoutConstraints c = new TableLayoutConstraints(
1670: cr1[C], cr1[R], cr2[C], cr2[R], alignment[C],
1671: alignment[R]);
1672:
1673: return "(" + c + ") " + component;
1674: }
1675: }
1676:
1677: }
|