0001: /*
0002: * Copyright (c) 2002-2004 JGoodies Karsten Lentzsch. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030:
0031: package com.jgoodies.forms.layout;
0032:
0033: import java.awt.Component;
0034: import java.awt.Insets;
0035: import java.awt.Rectangle;
0036: import java.io.Serializable;
0037: import java.util.StringTokenizer;
0038:
0039: /**
0040: * Defines constraints for components that are layed out with the FormLayout.
0041: * Defines the components display area: grid x, grid y, grid width
0042: * (column span), grid height (row span), horizontal alignment and vertical
0043: * alignment.
0044: * <p>
0045: *
0046: * Most methods return <em>this</em> object to enable method chaining.
0047: * <p>
0048: *
0049: * You can set optional insets in a constructor. This is useful if you need to
0050: * use a pixel-size insets to align perceived component bounds with pixel data,
0051: * for example an icon. Anyway, this is rarely used. The insets don't affect the
0052: * size computation for columns and rows. I consider renaming the insets to
0053: * offsets to better indicate the motivation for this option.
0054: * <p>
0055: *
0056: * <strong>Examples</strong>:<br>
0057: * The following cell constraints locate a component in the third column of the
0058: * fifth row; column and row span are 1; the component will be aligned with the
0059: * column's right-hand side and the row's bottom.
0060: *
0061: * <pre>
0062: * CellConstraints cc = new CellConstraints();
0063: * cc.xy(3, 5);
0064: * cc.xy(3, 5, CellConstraints.RIGHT, CellConstraints.BOTTOM);
0065: * cc.xy(3, 5, "right, bottom");
0066: *
0067: * cc.xyw(3, 5, 1);
0068: * cc.xyw(3, 5, 1, CellConstraints.RIGHT, CellConstraints.BOTTOM);
0069: * cc.xyw(3, 5, 1, "right, bottom");
0070: *
0071: * cc.xywh(3, 5, 1, 1);
0072: * cc.xywh(3, 5, 1, 1, CellConstraints.RIGHT, CellConstraints.BOTTOM);
0073: * cc.xywh(3, 5, 1, 1, "right, bottom");
0074: * </pre>
0075: *
0076: * See also the examples in the {@link FormLayout} class comment.
0077: * <p>
0078: *
0079: * TODO: Consider renaming the inset to offsets.
0080: *
0081: * @author Karsten Lentzsch
0082: * @version $Revision: 1.2 $
0083: */
0084: public final class CellConstraints implements Cloneable, Serializable {
0085:
0086: // Alignment Constants *************************************************
0087:
0088: /*
0089: * Implementation Note: Do not change the order of the following constants.
0090: * The serialization of class Alignment is ordinal-based and relies on it.
0091: */
0092:
0093: /**
0094: * Use the column's or row's default alignment.
0095: */
0096: public static final Alignment DEFAULT = new Alignment("default",
0097: Alignment.BOTH);
0098:
0099: /**
0100: * Fill the cell either horizontally or vertically.
0101: */
0102: public static final Alignment FILL = new Alignment("fill",
0103: Alignment.BOTH);
0104:
0105: /**
0106: * Put the component in the left.
0107: */
0108: public static final Alignment LEFT = new Alignment("left",
0109: Alignment.HORIZONTAL);
0110:
0111: /**
0112: * Put the component in the right.
0113: */
0114: public static final Alignment RIGHT = new Alignment("right",
0115: Alignment.HORIZONTAL);
0116:
0117: /**
0118: * Put the component in the center.
0119: */
0120: public static final Alignment CENTER = new Alignment("center",
0121: Alignment.BOTH);
0122:
0123: /**
0124: * Put the component in the top.
0125: */
0126: public static final Alignment TOP = new Alignment("top",
0127: Alignment.VERTICAL);
0128:
0129: /**
0130: * Put the component in the bottom.
0131: */
0132: public static final Alignment BOTTOM = new Alignment("bottom",
0133: Alignment.VERTICAL);
0134:
0135: /**
0136: * An array of all enumeration values used to canonicalize deserialized
0137: * alignments.
0138: */
0139: private static final Alignment[] VALUES = { DEFAULT, FILL, LEFT,
0140: RIGHT, CENTER, TOP, BOTTOM };
0141:
0142: /**
0143: * A reusable <code>Insets</code> object to reduce object instantiation.
0144: */
0145: private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
0146:
0147: // Fields ***************************************************************
0148:
0149: /**
0150: * Describes the component's horizontal grid origin (starts at 1).
0151: */
0152: public int gridX;
0153:
0154: /**
0155: * Describes the component's vertical grid origin (starts at 1).
0156: */
0157: public int gridY;
0158:
0159: /**
0160: * Describes the component's horizontal grid extend (number of cells).
0161: */
0162: public int gridWidth;
0163:
0164: /**
0165: * Describes the component's vertical grid extent (number of cells).
0166: */
0167: public int gridHeight;
0168:
0169: /**
0170: * Describes the component's horizontal alignment.
0171: */
0172: public Alignment hAlign;
0173:
0174: /**
0175: * Describes the component's vertical alignment.
0176: */
0177: public Alignment vAlign;
0178:
0179: /**
0180: * Describes the component's <code>Insets</code> in it's display area.
0181: */
0182: public Insets insets;
0183:
0184: // Instance Creation ****************************************************
0185:
0186: /**
0187: * Constructs a default instance of <code>CellConstraints</code>.
0188: */
0189: public CellConstraints() {
0190: this (1, 1);
0191: }
0192:
0193: /**
0194: * Constructs an instance of <code>CellConstraints</code> for the given
0195: * cell position.
0196: * <p>
0197: *
0198: * <strong>Examples:</strong>
0199: *
0200: * <pre>
0201: * new CellConstraints(1, 3);
0202: * new CellConstraints(1, 3);
0203: * </pre>
0204: *
0205: * @param gridX
0206: * the component's horizontal grid origin
0207: * @param gridY
0208: * the component's vertical grid origin
0209: */
0210: public CellConstraints(int gridX, int gridY) {
0211: this (gridX, gridY, 1, 1);
0212: }
0213:
0214: /**
0215: * Constructs an instance of <code>CellConstraints</code> for the given
0216: * cell position, anchor, and fill.
0217: * <p>
0218: *
0219: * <strong>Examples:</strong>
0220: *
0221: * <pre>
0222: * new CellConstraints(1, 3, CellConstraints.LEFT, CellConstraints.BOTTOM);
0223: * new CellConstraints(1, 3, CellConstraints.CENTER, CellConstraints.FILL);
0224: * </pre>
0225: *
0226: * @param gridX
0227: * the component's horizontal grid origin
0228: * @param gridY
0229: * the component's vertical grid origin
0230: * @param hAlign
0231: * the component's horizontal alignment
0232: * @param vAlign
0233: * the component's vertical alignment
0234: */
0235: public CellConstraints(int gridX, int gridY, Alignment hAlign,
0236: Alignment vAlign) {
0237: this (gridX, gridY, 1, 1, hAlign, vAlign, EMPTY_INSETS);
0238: }
0239:
0240: /**
0241: * Constructs an instance of <code>CellConstraints</code> for the given
0242: * cell position and size.
0243: * <p>
0244: *
0245: * <strong>Examples:</strong>
0246: *
0247: * <pre>
0248: * new CellConstraints(1, 3, 2, 1);
0249: * new CellConstraints(1, 3, 7, 3);
0250: * </pre>
0251: *
0252: * @param gridX
0253: * the component's horizontal grid origin
0254: * @param gridY
0255: * the component's vertical grid origin
0256: * @param gridWidth
0257: * the component's horizontal extent
0258: * @param gridHeight
0259: * the component's vertical extent
0260: */
0261: public CellConstraints(int gridX, int gridY, int gridWidth,
0262: int gridHeight) {
0263: this (gridX, gridY, gridWidth, gridHeight, DEFAULT, DEFAULT);
0264: }
0265:
0266: /**
0267: * Constructs an instance of <code>CellConstraints</code> for the given
0268: * cell position and size, anchor, and fill.
0269: * <p>
0270: *
0271: * <strong>Examples:</strong>
0272: *
0273: * <pre>
0274: * new CellConstraints(1, 3, 2, 1, CellConstraints.LEFT, CellConstraints.BOTTOM);
0275: * new CellConstraints(1, 3, 7, 3, CellConstraints.CENTER, CellConstraints.FILL);
0276: * </pre>
0277: *
0278: * @param gridX
0279: * the component's horizontal grid origin
0280: * @param gridY
0281: * the component's vertical grid origin
0282: * @param gridWidth
0283: * the component's horizontal extent
0284: * @param gridHeight
0285: * the component's vertical extent
0286: * @param hAlign
0287: * the component's horizontal alignment
0288: * @param vAlign
0289: * the component's vertical alignment
0290: */
0291: public CellConstraints(int gridX, int gridY, int gridWidth,
0292: int gridHeight, Alignment hAlign, Alignment vAlign) {
0293: this (gridX, gridY, gridWidth, gridHeight, hAlign, vAlign,
0294: EMPTY_INSETS);
0295: }
0296:
0297: /**
0298: * Constructs an instance of <code>CellConstraints</code> for the complete
0299: * set of available properties.
0300: * <p>
0301: *
0302: * <strong>Examples:</strong>
0303: *
0304: * <pre>
0305: * new CellConstraints(1, 3, 2, 1, CellConstraints.LEFT, CellConstraints.BOTTOM, new Insets(0, 1, 0, 3));
0306: * new CellConstraints(1, 3, 7, 3, CellConstraints.CENTER, CellConstraints.FILL, new Insets(0, 1, 0, 0));
0307: * </pre>
0308: *
0309: * @param gridX
0310: * the component's horizontal grid origin
0311: * @param gridY
0312: * the component's vertical grid origin
0313: * @param gridWidth
0314: * the component's horizontal extent
0315: * @param gridHeight
0316: * the component's vertical extent
0317: * @param hAlign
0318: * the component's horizontal alignment
0319: * @param vAlign
0320: * the component's vertical alignment
0321: * @param insets
0322: * the component's display area <code>Insets</code>
0323: * @throws IndexOutOfBoundsException
0324: * if the grid origin or extent is negative
0325: * @throws NullPointerException
0326: * if the horizontal or vertical alignment is null
0327: * @throws IllegalArgumentException
0328: * if an alignment orientation is invalid
0329: */
0330: public CellConstraints(int gridX, int gridY, int gridWidth,
0331: int gridHeight, Alignment hAlign, Alignment vAlign,
0332: Insets insets) {
0333: this .gridX = gridX;
0334: this .gridY = gridY;
0335: this .gridWidth = gridWidth;
0336: this .gridHeight = gridHeight;
0337: this .hAlign = hAlign;
0338: this .vAlign = vAlign;
0339: this .insets = insets;
0340: if (gridX <= 0)
0341: throw new IndexOutOfBoundsException(
0342: "The grid x must be a positive number.");
0343: if (gridY <= 0)
0344: throw new IndexOutOfBoundsException(
0345: "The grid y must be a positive number.");
0346: if (gridWidth <= 0)
0347: throw new IndexOutOfBoundsException(
0348: "The grid width must be a positive number.");
0349: if (gridHeight <= 0)
0350: throw new IndexOutOfBoundsException(
0351: "The grid height must be a positive number.");
0352: if (hAlign == null)
0353: throw new NullPointerException(
0354: "The horizontal alignment must not be null.");
0355: if (vAlign == null)
0356: throw new NullPointerException(
0357: "The vertical alignment must not be null.");
0358: ensureValidOrientations(hAlign, vAlign);
0359: }
0360:
0361: /**
0362: * Constructs an instance of <code>CellConstraints</code> from the given
0363: * encoded string properties.
0364: * <p>
0365: *
0366: * <strong>Examples:</strong>
0367: *
0368: * <pre>
0369: * new CellConstraints("1, 3");
0370: * new CellConstraints("1, 3, left, bottom");
0371: * new CellConstraints("1, 3, 2, 1, left, bottom");
0372: * new CellConstraints("1, 3, 2, 1, l, b");
0373: * </pre>
0374: *
0375: * @param encodedConstraints
0376: * the constraints encoded as string
0377: */
0378: public CellConstraints(String encodedConstraints) {
0379: this ();
0380: initFromConstraints(encodedConstraints);
0381: }
0382:
0383: // Setters **************************************************************
0384:
0385: /**
0386: * Sets row and column origins; sets width and height to 1; uses the default
0387: * alignments.
0388: * <p>
0389: *
0390: * <strong>Examples:</strong>
0391: *
0392: * <pre>
0393: * cc.xy(1, 1);
0394: * cc.xy(1, 3);
0395: * </pre>
0396: *
0397: * @param col
0398: * the new column index
0399: * @param row
0400: * the new row index
0401: * @return this
0402: */
0403: public CellConstraints xy(int col, int row) {
0404: return xywh(col, row, 1, 1);
0405: }
0406:
0407: /**
0408: * Sets row and column origins; sets width and height to 1; decodes
0409: * horizontal and vertical alignments from the given string.
0410: * <p>
0411: *
0412: * <strong>Examples:</strong>
0413: *
0414: * <pre>
0415: * cc.xy(1, 3, "left, bottom");
0416: * cc.xy(1, 3, "l, b");
0417: * cc.xy(1, 3, "center, fill");
0418: * cc.xy(1, 3, "c, f");
0419: * </pre>
0420: *
0421: * @param col
0422: * the new column index
0423: * @param row
0424: * the new row index
0425: * @param encodedAlignments
0426: * describes the horizontal and vertical alignments
0427: * @return this
0428: * @throws IllegalArgumentException
0429: * if an alignment orientation is invalid
0430: */
0431: public CellConstraints xy(int col, int row, String encodedAlignments) {
0432: return xywh(col, row, 1, 1, encodedAlignments);
0433: }
0434:
0435: /**
0436: * Sets the row and column origins; sets width and height to 1; set
0437: * horizontal and vertical alignment using the specified objects.
0438: * <p>
0439: *
0440: * <strong>Examples:</strong>
0441: *
0442: * <pre>
0443: * cc.xy(1, 3, CellConstraints.LEFT, CellConstraints.BOTTOM);
0444: * cc.xy(1, 3, CellConstraints.CENTER, CellConstraints.FILL);
0445: * </pre>
0446: *
0447: * @param col
0448: * the new column index
0449: * @param row
0450: * the new row index
0451: * @param colAlign
0452: * horizontal component alignment
0453: * @param rowAlign
0454: * vertical component alignment
0455: * @return this
0456: */
0457: public CellConstraints xy(int col, int row, Alignment colAlign,
0458: Alignment rowAlign) {
0459: return xywh(col, row, 1, 1, colAlign, rowAlign);
0460: }
0461:
0462: /**
0463: * Sets the row, column, width, and height; uses a height (row span) of 1
0464: * and the horizontal and vertical default alignments.
0465: * <p>
0466: *
0467: * <strong>Examples:</strong>
0468: *
0469: * <pre>
0470: * cc.xyw(1, 3, 7);
0471: * cc.xyw(1, 3, 2);
0472: * </pre>
0473: *
0474: * @param col
0475: * the new column index
0476: * @param row
0477: * the new row index
0478: * @param colSpan
0479: * the column span or grid width
0480: * @return this
0481: */
0482: public CellConstraints xyw(int col, int row, int colSpan) {
0483: return xywh(col, row, colSpan, 1, DEFAULT, DEFAULT);
0484: }
0485:
0486: /**
0487: * Sets the row, column, width, and height; decodes the horizontal and
0488: * vertical alignments from the given string. The row span (height) is set
0489: * to 1.
0490: * <p>
0491: *
0492: * <strong>Examples:</strong>
0493: *
0494: * <pre>
0495: * cc.xyw(1, 3, 7, "left, bottom");
0496: * cc.xyw(1, 3, 7, "l, b");
0497: * cc.xyw(1, 3, 2, "center, fill");
0498: * cc.xyw(1, 3, 2, "c, f");
0499: * </pre>
0500: *
0501: * @param col
0502: * the new column index
0503: * @param row
0504: * the new row index
0505: * @param colSpan
0506: * the column span or grid width
0507: * @param encodedAlignments
0508: * describes the horizontal and vertical alignments
0509: * @return this
0510: * @throws IllegalArgumentException
0511: * if an alignment orientation is invalid
0512: */
0513: public CellConstraints xyw(int col, int row, int colSpan,
0514: String encodedAlignments) {
0515: return xywh(col, row, colSpan, 1, encodedAlignments);
0516: }
0517:
0518: /**
0519: * Sets the row, column, width, and height; sets the horizontal and vertical
0520: * aligment using the specified alignment objects. The row span (height) is
0521: * set to 1.
0522: * <p>
0523: *
0524: * <strong>Examples:</strong>
0525: *
0526: * <pre>
0527: * cc.xyw(1, 3, 2, CellConstraints.LEFT, CellConstraints.BOTTOM);
0528: * cc.xyw(1, 3, 7, CellConstraints.CENTER, CellConstraints.FILL);
0529: * </pre>
0530: *
0531: * @param col
0532: * the new column index
0533: * @param row
0534: * the new row index
0535: * @param colSpan
0536: * the column span or grid width
0537: * @param colAlign
0538: * horizontal component alignment
0539: * @param rowAlign
0540: * vertical component alignment
0541: * @return this
0542: * @throws IllegalArgumentException
0543: * if an alignment orientation is invalid
0544: */
0545: public CellConstraints xyw(int col, int row, int colSpan,
0546: Alignment colAlign, Alignment rowAlign) {
0547: return xywh(col, row, colSpan, 1, colAlign, rowAlign);
0548: }
0549:
0550: /**
0551: * Sets the row, column, width, and height; uses default alignments.
0552: * <p>
0553: *
0554: * <strong>Examples:</strong>
0555: *
0556: * <pre>
0557: * cc.xywh(1, 3, 2, 1);
0558: * cc.xywh(1, 3, 7, 3);
0559: * </pre>
0560: *
0561: * @param col
0562: * the new column index
0563: * @param row
0564: * the new row index
0565: * @param colSpan
0566: * the column span or grid width
0567: * @param rowSpan
0568: * the row span or grid height
0569: * @return this
0570: */
0571: public CellConstraints xywh(int col, int row, int colSpan,
0572: int rowSpan) {
0573: return xywh(col, row, colSpan, rowSpan, DEFAULT, DEFAULT);
0574: }
0575:
0576: /**
0577: * Sets the row, column, width, and height; decodes the horizontal and
0578: * vertical alignments from the given string.
0579: * <p>
0580: *
0581: * <strong>Examples:</strong>
0582: *
0583: * <pre>
0584: * cc.xywh(1, 3, 2, 1, "left, bottom");
0585: * cc.xywh(1, 3, 2, 1, "l, b");
0586: * cc.xywh(1, 3, 7, 3, "center, fill");
0587: * cc.xywh(1, 3, 7, 3, "c, f");
0588: * </pre>
0589: *
0590: * @param col
0591: * the new column index
0592: * @param row
0593: * the new row index
0594: * @param colSpan
0595: * the column span or grid width
0596: * @param rowSpan
0597: * the row span or grid height
0598: * @param encodedAlignments
0599: * describes the horizontal and vertical alignments
0600: * @return this
0601: * @throws IllegalArgumentException
0602: * if an alignment orientation is invalid
0603: */
0604: public CellConstraints xywh(int col, int row, int colSpan,
0605: int rowSpan, String encodedAlignments) {
0606: CellConstraints result = xywh(col, row, colSpan, rowSpan);
0607: result.setAlignments(encodedAlignments);
0608: return result;
0609: }
0610:
0611: /**
0612: * Sets the row, column, width, and height; sets the horizontal and vertical
0613: * aligment using the specified alignment objects.
0614: * <p>
0615: *
0616: * <strong>Examples:</strong>
0617: *
0618: * <pre>
0619: * cc.xywh(1, 3, 2, 1, CellConstraints.LEFT, CellConstraints.BOTTOM);
0620: * cc.xywh(1, 3, 7, 3, CellConstraints.CENTER, CellConstraints.FILL);
0621: * </pre>
0622: *
0623: * @param col
0624: * the new column index
0625: * @param row
0626: * the new row index
0627: * @param colSpan
0628: * the column span or grid width
0629: * @param rowSpan
0630: * the row span or grid height
0631: * @param colAlign
0632: * horizontal component alignment
0633: * @param rowAlign
0634: * vertical component alignment
0635: * @return this
0636: * @throws IllegalArgumentException
0637: * if an alignment orientation is invalid
0638: */
0639: public CellConstraints xywh(int col, int row, int colSpan,
0640: int rowSpan, Alignment colAlign, Alignment rowAlign) {
0641: this .gridX = col;
0642: this .gridY = row;
0643: this .gridWidth = colSpan;
0644: this .gridHeight = rowSpan;
0645: this .hAlign = colAlign;
0646: this .vAlign = rowAlign;
0647: ensureValidOrientations(hAlign, vAlign);
0648: return this ;
0649: }
0650:
0651: // Parsing and Decoding String Descriptions *****************************
0652:
0653: /**
0654: * Decodes and returns the grid bounds and alignments for this constraints
0655: * as an array of six integers. The string representation is a comma
0656: * separated sequence, one of
0657: *
0658: * <pre>
0659: * "x, y"
0660: * "x, y, w, h"
0661: * "x, y, hAlign, vAlign"
0662: * "x, y, w, h, hAlign, vAlign"
0663: * </pre>
0664: *
0665: * @param encodedConstraints
0666: * represents horizontal and vertical alignment
0667: * @throws IllegalArgumentException
0668: * if the encoded constraints do not follow the constraint
0669: * syntax
0670: */
0671: private void initFromConstraints(String encodedConstraints) {
0672: StringTokenizer tokenizer = new StringTokenizer(
0673: encodedConstraints, " ,");
0674: int argCount = tokenizer.countTokens();
0675: if (!(argCount == 2 || argCount == 4 || argCount == 6))
0676: throw new IllegalArgumentException(
0677: "You must provide 2, 4 or 6 arguments.");
0678:
0679: Integer nextInt = decodeInt(tokenizer.nextToken());
0680: if (nextInt == null) {
0681: throw new IllegalArgumentException(
0682: "First cell constraint element must be a number.");
0683: }
0684: gridX = nextInt.intValue();
0685: if (gridX <= 0)
0686: throw new IndexOutOfBoundsException(
0687: "The grid x must be a positive number.");
0688:
0689: nextInt = decodeInt(tokenizer.nextToken());
0690: if (nextInt == null) {
0691: throw new IllegalArgumentException(
0692: "Second cell constraint element must be a number.");
0693: }
0694: gridY = nextInt.intValue();
0695: if (gridY <= 0)
0696: throw new IndexOutOfBoundsException(
0697: "The grid y must be a positive number.");
0698:
0699: if (!tokenizer.hasMoreTokens())
0700: return;
0701:
0702: String token = tokenizer.nextToken();
0703: nextInt = decodeInt(token);
0704: if (nextInt != null) {
0705: // Case: "x, y, w, h" or
0706: // "x, y, w, h, hAlign, vAlign"
0707: gridWidth = nextInt.intValue();
0708: if (gridWidth <= 0)
0709: throw new IndexOutOfBoundsException(
0710: "The grid width must be a positive number.");
0711: nextInt = decodeInt(tokenizer.nextToken());
0712: if (nextInt == null)
0713: throw new IllegalArgumentException(
0714: "Fourth cell constraint element must be like third.");
0715: gridHeight = nextInt.intValue();
0716: if (gridHeight <= 0)
0717: throw new IndexOutOfBoundsException(
0718: "The grid height must be a positive number.");
0719:
0720: if (!tokenizer.hasMoreTokens())
0721: return;
0722: token = tokenizer.nextToken();
0723: }
0724:
0725: hAlign = decodeAlignment(token);
0726: vAlign = decodeAlignment(tokenizer.nextToken());
0727: ensureValidOrientations(hAlign, vAlign);
0728: }
0729:
0730: /**
0731: * Decodes a string description for the horizontal and vertical alignment
0732: * and set the alignment values.
0733: * <p>
0734: * Valid horizontal aligmnents are: left, middle, right, default, and fill.
0735: * Valid vertical alignments are: top, center, bottom, default, and fill.
0736: * The anchor's string representation abbreviates the alignment: l, m, r, d,
0737: * f, t, c, and b.
0738: * <p>
0739: * Anchor examples: "mc" is centered, "lt" is northwest, "mt" is north, "rc"
0740: * east. "md" is horizontally centered and uses the row's default alignment.
0741: * "dt" is on top of the cell and uses the column's default alignment.
0742: * <p>
0743: *
0744: * @param encodedAlignments
0745: * represents horizontal and vertical alignment
0746: * @throws IllegalArgumentException
0747: * if an alignment orientation is invalid
0748: */
0749: private void setAlignments(String encodedAlignments) {
0750: StringTokenizer tokenizer = new StringTokenizer(
0751: encodedAlignments, " ,");
0752: hAlign = decodeAlignment(tokenizer.nextToken());
0753: vAlign = decodeAlignment(tokenizer.nextToken());
0754: ensureValidOrientations(hAlign, vAlign);
0755: }
0756:
0757: /**
0758: * Decodes an integer string representation and returns the associated
0759: * Integer or null in case of an invalid number format.
0760: *
0761: * @param token
0762: * the encoded integer
0763: * @return the decoded Integer or null
0764: */
0765: private Integer decodeInt(String token) {
0766: try {
0767: return Integer.decode(token);
0768: } catch (NumberFormatException e) {
0769: return null;
0770: }
0771: }
0772:
0773: /**
0774: * Parses an alignment string description and returns the corresponding
0775: * alignment value.
0776: *
0777: * @param encodedAlignment
0778: * the encoded alignment
0779: * @return the associated <code>Alignment</code> instance
0780: */
0781: private Alignment decodeAlignment(String encodedAlignment) {
0782: return Alignment.valueOf(encodedAlignment);
0783: }
0784:
0785: /**
0786: * Checks and verifies that this constraints object has valid grid index
0787: * values, i. e. the display area cells are inside the form's grid.
0788: *
0789: * @param colCount
0790: * number of columns in the grid
0791: * @param rowCount
0792: * number of rows in the grid
0793: * @throws IndexOutOfBoundsException
0794: * if the display area described by this constraints object is
0795: * not inside the grid
0796: */
0797: void ensureValidGridBounds(int colCount, int rowCount) {
0798: if (gridX <= 0) {
0799: throw new IndexOutOfBoundsException("The column index "
0800: + gridX + " must be positive.");
0801: }
0802: if (gridX > colCount) {
0803: throw new IndexOutOfBoundsException("The column index "
0804: + gridX + " must be less than or equal to "
0805: + colCount + ".");
0806: }
0807: if (gridX + gridWidth - 1 > colCount) {
0808: throw new IndexOutOfBoundsException("The grid width "
0809: + gridWidth + " must be less than or equal to "
0810: + (colCount - gridX + 1) + ".");
0811: }
0812: if (gridY <= 0) {
0813: throw new IndexOutOfBoundsException("The row index "
0814: + gridY + " must be positive.");
0815: }
0816: if (gridY > rowCount) {
0817: throw new IndexOutOfBoundsException("The row index "
0818: + gridY + " must be less than or equal to "
0819: + rowCount + ".");
0820: }
0821: if (gridY + gridHeight - 1 > rowCount) {
0822: throw new IndexOutOfBoundsException("The grid height "
0823: + gridHeight + " must be less than or equal to "
0824: + (rowCount - gridY + 1) + ".");
0825: }
0826: }
0827:
0828: /**
0829: * Checks and verifies that the horizontal alignment is a horizontal and the
0830: * vertical alignment is vertical.
0831: *
0832: * @param horizontalAlignment
0833: * the horizontal alignment
0834: * @param verticalAlignment
0835: * the vertical alignment
0836: * @throws IllegalArgumentException
0837: * if an alignment is invalid
0838: */
0839: private void ensureValidOrientations(Alignment horizontalAlignment,
0840: Alignment verticalAlignment) {
0841: if (!horizontalAlignment.isHorizontal())
0842: throw new IllegalArgumentException(
0843: "The horizontal alignment must be one of: left, center, right, fill, default.");
0844: if (!verticalAlignment.isVertical())
0845: throw new IllegalArgumentException(
0846: "The vertical alignment must be one of: top, center, botto, fill, default.");
0847: }
0848:
0849: // Settings Component Bounds ********************************************
0850:
0851: /**
0852: * Sets the component's bounds using the given component and cell bounds.
0853: *
0854: * @param c
0855: * the component to set bounds
0856: * @param layout
0857: * the FormLayout instance that computes the bounds
0858: * @param cellBounds
0859: * the cell's bounds
0860: * @param minWidthMeasure
0861: * measures the minimum width
0862: * @param minHeightMeasure
0863: * measures the minimum height
0864: * @param prefWidthMeasure
0865: * measures the preferred width
0866: * @param prefHeightMeasure
0867: * measures the preferred height
0868: */
0869: void setBounds(Component c, FormLayout layout,
0870: Rectangle cellBounds, FormLayout.Measure minWidthMeasure,
0871: FormLayout.Measure minHeightMeasure,
0872: FormLayout.Measure prefWidthMeasure,
0873: FormLayout.Measure prefHeightMeasure) {
0874: ColumnSpec colSpec = gridWidth == 1 ? layout
0875: .getColumnSpec(gridX) : null;
0876: RowSpec rowSpec = gridHeight == 1 ? layout.getRowSpec(gridY)
0877: : null;
0878: Alignment concreteHAlign = concreteAlignment(this .hAlign,
0879: colSpec);
0880: Alignment concreteVAlign = concreteAlignment(this .vAlign,
0881: rowSpec);
0882: Insets concreteInsets = this .insets != null ? this .insets
0883: : EMPTY_INSETS;
0884: int cellX = cellBounds.x + concreteInsets.left;
0885: int cellY = cellBounds.y + concreteInsets.top;
0886: int cellW = cellBounds.width - concreteInsets.left
0887: - concreteInsets.right;
0888: int cellH = cellBounds.height - concreteInsets.top
0889: - concreteInsets.bottom;
0890: int compW = componentSize(c, colSpec, cellW, minWidthMeasure,
0891: prefWidthMeasure);
0892: int compH = componentSize(c, rowSpec, cellH, minHeightMeasure,
0893: prefHeightMeasure);
0894: int x = origin(concreteHAlign, cellX, cellW, compW);
0895: int y = origin(concreteVAlign, cellY, cellH, compH);
0896: int w = extent(concreteHAlign, cellW, compW);
0897: int h = extent(concreteVAlign, cellH, compH);
0898: c.setBounds(x, y, w, h);
0899: }
0900:
0901: /**
0902: * Computes and returns the concrete alignment. Takes into account the cell
0903: * alignment and <i>the</i> <code>FormSpec</code> if applicable.
0904: * <p>
0905: *
0906: * If this constraints object doesn't belong to a single column or row, the
0907: * <code>formSpec</code> parameter is <code>null</code>. In this case
0908: * the cell alignment is answered, but <code>DEFAULT</code> is mapped to
0909: * <code>FILL</code>.
0910: * <p>
0911: *
0912: * If the cell belongs to a single column or row, we use the cell alignment,
0913: * unless it is <code>DEFAULT</code>, where the alignment is inherited
0914: * from the column or row resp.
0915: *
0916: * @param cellAlignment
0917: * this cell's aligment
0918: * @param formSpec
0919: * the associated column or row specification
0920: * @return the concrete alignment
0921: */
0922: private Alignment concreteAlignment(Alignment cellAlignment,
0923: FormSpec formSpec) {
0924: return formSpec == null ? (cellAlignment == DEFAULT ? FILL
0925: : cellAlignment) : usedAlignment(cellAlignment,
0926: formSpec);
0927: }
0928:
0929: /**
0930: * Returns the alignment used for a given form constraints object. The cell
0931: * alignment overrides the column or row default, unless it is
0932: * <code>DEFAULT</code>. In the latter case, we use the column or row
0933: * alignment.
0934: *
0935: * @param cellAlignment
0936: * this cell constraint's alignment
0937: * @param formSpec
0938: * the associated column or row specification
0939: * @return the alignment used
0940: */
0941: private Alignment usedAlignment(Alignment cellAlignment,
0942: FormSpec formSpec) {
0943: if (cellAlignment != CellConstraints.DEFAULT) {
0944: // Cell alignments other than DEFAULT override col/row alignments
0945: return cellAlignment;
0946: }
0947: FormSpec.DefaultAlignment defaultAlignment = formSpec
0948: .getDefaultAlignment();
0949: if (defaultAlignment == FormSpec.FILL_ALIGN)
0950: return FILL;
0951: if (defaultAlignment == ColumnSpec.LEFT)
0952: return LEFT;
0953: else if (defaultAlignment == FormSpec.CENTER_ALIGN)
0954: return CENTER;
0955: else if (defaultAlignment == ColumnSpec.RIGHT)
0956: return RIGHT;
0957: else if (defaultAlignment == RowSpec.TOP)
0958: return TOP;
0959: else
0960: return BOTTOM;
0961: }
0962:
0963: /**
0964: * Computes and returns the pixel size of the given component using the
0965: * given form specification, measures, and cell size.
0966: *
0967: * @param component
0968: * the component to measure
0969: * @param formSpec
0970: * the specification of the component's column/row
0971: * @param minMeasure
0972: * the measure for the minimum size
0973: * @param prefMeasure
0974: * the measure for the preferred size
0975: * @param cellSize
0976: * the cell size
0977: * @return the component size as measured or a constant
0978: */
0979: private int componentSize(Component component, FormSpec formSpec,
0980: int cellSize, FormLayout.Measure minMeasure,
0981: FormLayout.Measure prefMeasure) {
0982: if (formSpec == null) {
0983: return prefMeasure.sizeOf(component);
0984: } else if (formSpec.getSize() == Sizes.MINIMUM) {
0985: return minMeasure.sizeOf(component);
0986: } else if (formSpec.getSize() == Sizes.PREFERRED) {
0987: return prefMeasure.sizeOf(component);
0988: } else { // default mode
0989: return Math.min(cellSize, prefMeasure.sizeOf(component));
0990: }
0991: }
0992:
0993: /**
0994: * Computes and returns the component's pixel origin.
0995: *
0996: * @param alignment
0997: * the component's alignment
0998: * @param cellOrigin
0999: * the origin of the display area
1000: * @param cellSize
1001: * the extent of the display area
1002: * @param componentSize
1003: * @return the component's pixel origin
1004: */
1005: private int origin(Alignment alignment, int cellOrigin,
1006: int cellSize, int componentSize) {
1007: if (alignment == RIGHT || alignment == BOTTOM) {
1008: return cellOrigin + cellSize - componentSize;
1009: } else if (alignment == CENTER) {
1010: return cellOrigin + (cellSize - componentSize) / 2;
1011: } else { // left, top, fill
1012: return cellOrigin;
1013: }
1014: }
1015:
1016: /**
1017: * Returns the component's pixel extent.
1018: *
1019: * @param alignment
1020: * the component's alignment
1021: * @param cellSize
1022: * the size of the display area
1023: * @param componentSize
1024: * the component's size
1025: * @return the component's pixel extent
1026: */
1027: private int extent(Alignment alignment, int cellSize,
1028: int componentSize) {
1029: return alignment == FILL ? cellSize : componentSize;
1030: }
1031:
1032: // Misc *****************************************************************
1033:
1034: /**
1035: * Creates a copy of this cell constraints object.
1036: *
1037: * @return a copy of this cell constraints object
1038: */
1039: public Object clone() {
1040: try {
1041: CellConstraints c = (CellConstraints) super .clone();
1042: c.insets = (Insets) insets.clone();
1043: return c;
1044: } catch (CloneNotSupportedException e) {
1045: // This shouldn't happen, since we are Cloneable.
1046: throw new InternalError();
1047: }
1048: }
1049:
1050: /**
1051: * Constructs and returns a string representation of this constraints
1052: * object.
1053: *
1054: * @return string representation of this constraints object
1055: */
1056: public String toString() {
1057: StringBuffer buffer = new StringBuffer("CellConstraints");
1058: buffer.append("[x=");
1059: buffer.append(gridX);
1060: buffer.append("; y=");
1061: buffer.append(gridY);
1062: buffer.append("; w=");
1063: buffer.append(gridWidth);
1064: buffer.append("; h=");
1065: buffer.append(gridHeight);
1066: buffer.append("; hAlign=");
1067: buffer.append(hAlign);
1068: buffer.append("; vAlign=");
1069: buffer.append(vAlign);
1070: if (!(EMPTY_INSETS.equals(insets))) {
1071: buffer.append("; insets=");
1072: buffer.append(insets);
1073: }
1074:
1075: buffer.append(']');
1076: return buffer.toString();
1077: }
1078:
1079: /**
1080: * Returns a short string representation of this constraints object.
1081: *
1082: * @return a short string representation of this constraints object
1083: */
1084: public String toShortString() {
1085: return toShortString(null);
1086: }
1087:
1088: /**
1089: * Returns a short string representation of this constraints object. This
1090: * method can use the given <code>FormLayout</code> to display extra
1091: * information how default alignments are mapped to concrete alignments.
1092: * Therefore it asks the related column and row as specified by this
1093: * constraints object.
1094: *
1095: * @param layout
1096: * the layout to be presented as a string
1097: * @return a short string representation of this constraints object
1098: */
1099: public String toShortString(FormLayout layout) {
1100: StringBuffer buffer = new StringBuffer("(");
1101: buffer.append(formatInt(gridX));
1102: buffer.append(", ");
1103: buffer.append(formatInt(gridY));
1104: buffer.append(", ");
1105: buffer.append(formatInt(gridWidth));
1106: buffer.append(", ");
1107: buffer.append(formatInt(gridHeight));
1108: buffer.append(", \"");
1109: buffer.append(hAlign.abbreviation());
1110: if (hAlign == DEFAULT && layout != null) {
1111: buffer.append('=');
1112: ColumnSpec colSpec = gridWidth == 1 ? layout
1113: .getColumnSpec(gridX) : null;
1114: buffer.append(concreteAlignment(hAlign, colSpec)
1115: .abbreviation());
1116: }
1117: buffer.append(", ");
1118: buffer.append(vAlign.abbreviation());
1119: if (vAlign == DEFAULT && layout != null) {
1120: buffer.append('=');
1121: RowSpec rowSpec = gridHeight == 1 ? layout
1122: .getRowSpec(gridY) : null;
1123: buffer.append(concreteAlignment(vAlign, rowSpec)
1124: .abbreviation());
1125: }
1126: buffer.append("\"");
1127: if (!(EMPTY_INSETS.equals(insets))) {
1128: buffer.append(", ");
1129: buffer.append(insets);
1130: }
1131:
1132: buffer.append(')');
1133: return buffer.toString();
1134: }
1135:
1136: // Helper Class *********************************************************
1137:
1138: /**
1139: * An ordinal-based serializable typesafe enumeration for component
1140: * alignment types as used by the {@link FormLayout}.
1141: */
1142: public static final class Alignment implements Serializable {
1143:
1144: private static final int HORIZONTAL = 0;
1145: private static final int VERTICAL = 1;
1146: private static final int BOTH = 2;
1147:
1148: private final transient String name;
1149: private final transient int orientation;
1150:
1151: private Alignment(String name, int orientation) {
1152: this .name = name;
1153: this .orientation = orientation;
1154: }
1155:
1156: static Alignment valueOf(String nameOrAbbreviation) {
1157: String str = nameOrAbbreviation.toLowerCase();
1158: if (str.equals("d") || str.equals("default"))
1159: return DEFAULT;
1160: else if (str.equals("f") || str.equals("fill"))
1161: return FILL;
1162: else if (str.equals("c") || str.equals("center"))
1163: return CENTER;
1164: else if (str.equals("l") || str.equals("left"))
1165: return LEFT;
1166: else if (str.equals("r") || str.equals("right"))
1167: return RIGHT;
1168: else if (str.equals("t") || str.equals("top"))
1169: return TOP;
1170: else if (str.equals("b") || str.equals("bottom"))
1171: return BOTTOM;
1172: else
1173: throw new IllegalArgumentException(
1174: "Invalid alignment "
1175: + nameOrAbbreviation
1176: + ". Must be one of: left, center, right, top, bottom, "
1177: + "fill, default, l, c, r, t, b, f, d.");
1178: }
1179:
1180: /**
1181: * Returns this Alignment's name.
1182: *
1183: * @return this alignment's name.
1184: */
1185: public String toString() {
1186: return name;
1187: }
1188:
1189: /**
1190: * Returns the first character of this Alignment's name. Used to
1191: * identify it in short format strings.
1192: *
1193: * @return the name's first character.
1194: */
1195: public char abbreviation() {
1196: return name.charAt(0);
1197: }
1198:
1199: private boolean isHorizontal() {
1200: return orientation != VERTICAL;
1201: }
1202:
1203: private boolean isVertical() {
1204: return orientation != HORIZONTAL;
1205: }
1206:
1207: // Serialization
1208: // *********************************************************
1209:
1210: private static int nextOrdinal = 0;
1211:
1212: private final int ordinal = nextOrdinal++;
1213:
1214: private Object readResolve() {
1215: return VALUES[ordinal]; // Canonicalize
1216: }
1217:
1218: }
1219:
1220: /**
1221: * Returns an integer that has a minimum of two characters.
1222: *
1223: * @param number
1224: * the number to format
1225: * @return a string representation for a number with a minum of two chars
1226: */
1227: private String formatInt(int number) {
1228: String str = Integer.toString(number);
1229: return number < 10 ? " " + str : str;
1230: }
1231:
1232: }
|