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