0001: /*
0002: * ExtendedGridLayout.java - a grid layout manager with variable cell sizes
0003: * that supports colspans and rowspans
0004: * :tabSize=8:indentSize=8:noTabs=false:
0005: * :folding=explicit:collapseFolds=1:
0006: *
0007: * Originally written by Björn Kautler for the jEdit project. This work has been
0008: * placed into the public domain. You may use this work in any way and for any
0009: * purpose you wish.
0010: *
0011: * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
0012: * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
0013: * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
0014: * OR REDISTRIBUTION OF THIS SOFTWARE.
0015: */
0016:
0017: package org.gjt.sp.jedit.gui;
0018:
0019: import java.awt.Component;
0020: import java.awt.Container;
0021: import java.awt.Dimension;
0022: import java.awt.Insets;
0023: import java.awt.LayoutManager2;
0024:
0025: import java.util.ArrayList;
0026: import java.util.Arrays;
0027: import java.util.HashSet;
0028: import java.util.Hashtable;
0029: import java.util.Iterator;
0030: import java.util.List;
0031: import java.util.ListIterator;
0032: import java.util.Set;
0033:
0034: import static java.awt.Component.CENTER_ALIGNMENT;
0035:
0036: import static org.gjt.sp.jedit.gui.ExtendedGridLayoutConstraints.REMAINDER;
0037:
0038: /**
0039: * A layout manager that places components in a rectangular grid
0040: * with variable cell sizes that supports colspans and rowspans.
0041: * <p>
0042: * The container is divided into rectangles, and each component is placed
0043: * in a rectangular space defined by its colspan and rowspan.
0044: * Each row is as large as the largest component in
0045: * that row, and each column is as wide as the widest component in
0046: * that column. </p>
0047: * <p>
0048: * This behavior is similar to
0049: * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/GridLayout.html">{@code java.awt.GridLayout}</a>
0050: * but it supports different row heights and
0051: * column widths for each row/column. </p>
0052: * <p>
0053: * For example, the following is a Dialog that lays out ten buttons
0054: * exactly the same as in the example of the JavaDoc of
0055: * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/GridBagLayout.html">{@code java.awt.GridBagLayout}</a>
0056: * with the difference of vertical and horizontal gaps that can be configured:
0057: * <hr>
0058: * <blockquote><pre><font color="#000000">
0059: * <font color="#000000"> 1:</font><font color="#009966"><strong>import</strong></font> java.awt.Button;
0060: * <font color="#000000"> 2:</font><font color="#009966"><strong>import</strong></font> java.awt.Dimension;
0061: * <font color="#000000"> 3:</font>
0062: * <font color="#000000"> 4:</font><font color="#009966"><strong>import</strong></font> javax.swing.JDialog;
0063: * <font color="#990066"> 5:</font>
0064: * <font color="#000000"> 6:</font><font color="#009966"><strong>import</strong></font> org.gjt.sp.jedit.gui.ExtendedGridLayout;
0065: * <font color="#000000"> 7:</font><font color="#009966"><strong>import</strong></font> org.gjt.sp.jedit.gui.ExtendedGridLayoutConstraints;
0066: * <font color="#000000"> 8:</font>
0067: * <font color="#000000"> 9:</font><font color="#009966"><strong>import</strong></font> <font color="#006699"><strong>static</strong></font> org.gjt.sp.jedit.gui.ExtendedGridLayoutConstraints.REMAINDER;
0068: * <font color="#990066"> 10:</font>
0069: * <font color="#000000"> 11:</font><font color="#006699"><strong>public</strong></font> <font color="#0099ff"><strong>class</strong></font> ExampleDialog <font color="#006699"><strong>extends</strong></font> JDialog <font color="#000000"><strong>{</strong></font>
0070: * <font color="#000000"> 12:</font> <font color="#006699"><strong>public</strong></font> <font color="#9966ff">ExampleDialog</font>() <font color="#000000"><strong>{</strong></font>
0071: * <font color="#000000"> 13:</font> <font color="#cc00cc">super</font>(<font color="#cc00cc">null</font>,<font color="#ff00cc">"</font><font color="#ff00cc">Example</font><font color="#ff00cc"> </font><font color="#ff00cc">Dialog</font><font color="#ff00cc">"</font>,<font color="#cc00cc">true</font>);
0072: * <font color="#000000"> 14:</font> <font color="#9966ff">setLayout</font>(<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayout</font>(<font color="#ff0000">5</font>,<font color="#ff0000">5</font>,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">Insets</font>(<font color="#ff0000">5</font>,<font color="#ff0000">5</font>,<font color="#ff0000">5</font>,<font color="#ff0000">5</font>)));
0073: * <font color="#990066"> 15:</font>
0074: * <font color="#000000"> 16:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button1</font><font color="#ff00cc">"</font>));
0075: * <font color="#000000"> 17:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button2</font><font color="#ff00cc">"</font>));
0076: * <font color="#000000"> 18:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button3</font><font color="#ff00cc">"</font>));
0077: * <font color="#000000"> 19:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button4</font><font color="#ff00cc">"</font>));
0078: * <font color="#990066"> 20:</font> Button button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button5</font><font color="#ff00cc">"</font>);
0079: * <font color="#000000"> 21:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">1</font>,REMAINDER,<font color="#ff0000">1</font>,button));
0080: * <font color="#000000"> 22:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button6</font><font color="#ff00cc">"</font>);
0081: * <font color="#000000"> 23:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">2</font>,<font color="#ff0000">3</font>,<font color="#ff0000">1</font>,button));
0082: * <font color="#000000"> 24:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button7</font><font color="#ff00cc">"</font>);
0083: * <font color="#990066"> 25:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">2</font>,button));
0084: * <font color="#000000"> 26:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button8</font><font color="#ff00cc">"</font>);
0085: * <font color="#000000"> 27:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">3</font>,<font color="#ff0000">1</font>,<font color="#ff0000">2</font>,button));
0086: * <font color="#000000"> 28:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button9</font><font color="#ff00cc">"</font>);
0087: * <font color="#000000"> 29:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">3</font>,<font color="#ff0000">3</font>,<font color="#ff0000">1</font>,button));
0088: * <font color="#990066"> 30:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">"</font><font color="#ff00cc">Button10</font><font color="#ff00cc">"</font>);
0089: * <font color="#000000"> 31:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">4</font>,REMAINDER,<font color="#ff0000">1</font>,button));
0090: * <font color="#000000"> 32:</font>
0091: * <font color="#000000"> 33:</font> <font color="#9966ff">pack</font>();
0092: * <font color="#000000"> 34:</font> <font color="#9966ff">setLocationRelativeTo</font>(<font color="#cc00cc">null</font>);
0093: * <font color="#990066"> 35:</font> <font color="#9966ff">setVisible</font>(<font color="#cc00cc">true</font>);
0094: * <font color="#000000"> 36:</font> <font color="#000000"><strong>}</strong></font>
0095: * <font color="#000000"> 37:</font>
0096: * <font color="#000000"> 38:</font> <font color="#006699"><strong>private</strong></font> Button <font color="#9966ff">makeButton</font>(String name) <font color="#000000"><strong>{</strong></font>
0097: * <font color="#000000"> 39:</font> Button button <font color="#000000"><strong>=</strong></font> <font color="#006699"><strong>new</strong></font> <font color="#9966ff">Button</font>(name);
0098: * <font color="#990066"> 40:</font> button.<font color="#9966ff">setMaximumSize</font>(<font color="#006699"><strong>new</strong></font> <font color="#9966ff">Dimension</font>(Integer.MAX_VALUE,Integer.MAX_VALUE));
0099: * <font color="#000000"> 41:</font> <font color="#006699"><strong>return</strong></font> button;
0100: * <font color="#000000"> 42:</font> <font color="#000000"><strong>}</strong></font>
0101: * <font color="#000000"> 43:</font><font color="#000000"><strong>}</strong></font>
0102: * </font></pre></blockquote>
0103: * <hr>
0104: * If you use {@code REMAINDER} as colspan or rowspan then a component takes
0105: * up the remaining space in that column or row. Any additional components in
0106: * a row are ignored and not displayed. Additional components in a column are
0107: * moved rightside. If a rowspan hits a colspan, the colspan ends and the
0108: * rowspan takes precedence.
0109: * <p>
0110: * Components for which {@code isVisible() == false} are ignored. Because
0111: * of this, components can be replaced "in-place" by adding two components next to
0112: * each other, with different {@code isVisible()} values, and toggling the
0113: * {@code setVisible()} values of both when we wish to swap the currently
0114: * visible component with the one that is hidden. </p>
0115: *
0116: * <p>
0117: * If you want to reserve free space in a row inbetween components,
0118: * add a <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/Box.Filler.html">{@code javax.swing.Box.Filler}</a>
0119: * to the layout if the free space is in the middle of a row,
0120: * or just don't add components if the free space
0121: * should be at the end of a row.</p>
0122: * <p>
0123: * If a row is taller, or a column is wider than the {@code maximumSize} of a component,
0124: * the component is resized to its maximum size and aligned according to its
0125: * {@code alignmentX} and {@code alignmentY} values. </p>
0126: * <p>
0127: * One instance of this class can be used to layout multiple
0128: * containers at the same time. </p>
0129: *
0130: * @author Björn "Vampire" Kautler
0131: * @version 1.0
0132: * @see ExtendedGridLayoutConstraints
0133: * @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/Component.html"><code>java.awt.Component</code></a>
0134: * @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/Box.Filler.html"><code>javax.swing.Box.Filler</code></a>
0135: */
0136: public class ExtendedGridLayout implements LayoutManager2 {
0137: /**
0138: * This hashtable maintains the association between
0139: * a component and its ExtendedGridLayoutConstraints.
0140: * The Keys in {@code comptable} are the components and the
0141: * values are the instances of {@code ExtendedGridLayoutConstraints}.
0142: *
0143: * @see ExtendedGridLayoutConstraints
0144: */
0145: private final Hashtable<Component, ExtendedGridLayoutConstraints> comptable;
0146:
0147: /**
0148: * Specifies the horizontal space between two columns.
0149: * The default value is 0.
0150: *
0151: * @see #distanceToBorders
0152: * @see #vgap
0153: */
0154: private final int hgap;
0155:
0156: /**
0157: * Specifies the vertical space between two rows.
0158: * The default value is 0.
0159: *
0160: * @see #distanceToBorders
0161: * @see #hgap
0162: */
0163: private final int vgap;
0164:
0165: /**
0166: * Specifies the gap between the grid and the borders of the parent container.
0167: * The default value is 0 for all four borders.
0168: *
0169: * @see #hgap
0170: * @see #vgap
0171: */
0172: private final Insets distanceToBorders;
0173:
0174: /**
0175: * An enum to tell the {@code getSize()} method which size is requested.
0176: *
0177: * @see #getSize()
0178: */
0179: private static enum LayoutSize {
0180: MINIMUM, PREFERRED, MAXIMUM
0181: }
0182:
0183: /**
0184: * Creates an extended grid layout manager with the specified horizontal
0185: * and vertical gap, and the specified distance to the borders
0186: * of the parent container.
0187: *
0188: * @param hgap The horizontal space between two columns ({@literal >=0})
0189: * @param vgap The vertical space between two rows ({@literal >=0})
0190: * @param distanceToBorders The distances to the borders of the parent container
0191: * @throws IllegalArgumentException if hgap {@literal < 0}
0192: * @throws IllegalArgumentException if vgap {@literal < 0}
0193: */
0194: public ExtendedGridLayout(int hgap, int vgap,
0195: Insets distanceToBorders) {
0196: if (hgap < 0) {
0197: throw new IllegalArgumentException(
0198: "hgap must be non-negative (" + hgap + ')');
0199: }
0200: if (vgap < 0) {
0201: throw new IllegalArgumentException(
0202: "vgap must be non-negative (" + vgap + ')');
0203: }
0204: this .hgap = hgap;
0205: this .vgap = vgap;
0206: this .distanceToBorders = (Insets) distanceToBorders.clone();
0207: comptable = new Hashtable<Component, ExtendedGridLayoutConstraints>();
0208: }
0209:
0210: /**
0211: * Creates an extended grid layout manager with zero horizontal
0212: * and vertical gap, and zero distance to the borders
0213: * of the parent container.
0214: */
0215: public ExtendedGridLayout() {
0216: this (0, 0, new Insets(0, 0, 0, 0));
0217: }
0218:
0219: /**
0220: * If the layout manager uses a per-component string,
0221: * adds the component <code>component</code> to the layout,
0222: * associating it with the string specified by <code>name</code>.
0223: *
0224: * @param name The string to be associated with the component.
0225: * Has to be {@code null}, so that default constraints are used.
0226: * @param component The component to be added
0227: * @throws IllegalArgumentException if {@code name} is not {@code null}
0228: * @see #addLayoutComponent(java.awt.Component, java.lang.Object)
0229: */
0230: public void addLayoutComponent(String name, Component component) {
0231: addLayoutComponent(component, name);
0232: }
0233:
0234: /**
0235: * Adds the specified component to the layout, using the specified
0236: * constraints object.
0237: *
0238: * @param component The component to be added
0239: * @param constraints Where/how the component is added to the layout.
0240: * @throws IllegalArgumentException if {@code constraints} is not an ExtendedGridLayoutConstraints object
0241: * @throws IllegalArgumentException if {@code constraints} is a placeholder
0242: * @throws IllegalArgumentException if {@code constraints} is not the right one for the component
0243: * @see ExtendedGridLayoutConstraints
0244: */
0245: public void addLayoutComponent(Component component,
0246: Object constraints) {
0247: if (null == constraints) {
0248: constraints = new ExtendedGridLayoutConstraints(component);
0249: }
0250: if (constraints instanceof ExtendedGridLayoutConstraints) {
0251: ExtendedGridLayoutConstraints eglConstraints = (ExtendedGridLayoutConstraints) constraints;
0252: if (eglConstraints.isPlaceholder()) {
0253: throw new IllegalArgumentException(
0254: "constraints must not be a placeholder");
0255: } else if (component != eglConstraints.getComponent()) {
0256: throw new IllegalArgumentException(
0257: "constraints is not the right one for this component");
0258: }
0259: comptable.put(component, eglConstraints);
0260: } else {
0261: throw new IllegalArgumentException(
0262: "constraints must not be an ExtendedGridLayoutConstraints object");
0263: }
0264: }
0265:
0266: /**
0267: * Retrieves the constraints for the specified {@code component}.
0268: * If {@code component} is not in the {@code ExtendedGridLayout},
0269: * a set of default {@code ExtendedGridLayoutConstraints} are returned.
0270: *
0271: * @param component the {@code component} to be queried
0272: * @return the contraints for the specified {@code component}
0273: * @throws NullPointerException if {@code component} is {@code null}
0274: * @see ExtendedGridLayoutConstraints
0275: */
0276: private ExtendedGridLayoutConstraints lookupConstraints(
0277: Component component) {
0278: if (null == component) {
0279: throw new NullPointerException("component must not be null");
0280: }
0281: ExtendedGridLayoutConstraints constraints = comptable
0282: .get(component);
0283: if (null == constraints) {
0284: constraints = new ExtendedGridLayoutConstraints(component);
0285: comptable.put(component, constraints);
0286: }
0287: return constraints;
0288: }
0289:
0290: /**
0291: * Removes the specified component from the layout.
0292: *
0293: * @param component The component to be removed
0294: */
0295: public void removeLayoutComponent(Component component) {
0296: comptable.remove(component);
0297: }
0298:
0299: /**
0300: * Returns the alignment along the X axis. This specifies how
0301: * the component would like to be aligned relative to other
0302: * components. The value should be a number between 0 and 1
0303: * where 0 represents alignment along the origin, 1 is aligned
0304: * the furthest away from the origin, 0.5 is centered, etc.
0305: *
0306: * @param container The container for which the alignment should be returned
0307: * @return {@code java.awt.Component.CENTER_ALIGNMENT}
0308: */
0309: public float getLayoutAlignmentX(Container container) {
0310: return CENTER_ALIGNMENT;
0311: }
0312:
0313: /**
0314: * Returns the alignment along the Y axis. This specifies how
0315: * the component would like to be aligned relative to other
0316: * components. The value should be a number between 0 and 1
0317: * where 0 represents alignment along the origin, 1 is aligned
0318: * the furthest away from the origin, 0.5 is centered, etc.
0319: *
0320: * @param container The container for which the alignment should be returned
0321: * @return {@code java.awt.Component.CENTER_ALIGNMENT}
0322: */
0323: public float getLayoutAlignmentY(Container container) {
0324: return CENTER_ALIGNMENT;
0325: }
0326:
0327: /**
0328: * Calculates the minimum size dimensions for the specified
0329: * container, given the components it contains.
0330: *
0331: * @param parent The component to be laid out
0332: * @return The minimum size for the container
0333: * @see #maximumLayoutSize
0334: * @see #preferredLayoutSize
0335: */
0336: public Dimension minimumLayoutSize(Container parent) {
0337: synchronized (parent.getTreeLock()) {
0338: List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
0339: Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
0340: Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
0341: Dimension gridSize = buildGrid(parent, gridRows, colspans,
0342: rowspans);
0343: return getSize(parent, LayoutSize.MINIMUM, false, gridSize,
0344: gridRows, colspans, rowspans, new int[0][0]);
0345: }
0346: }
0347:
0348: /**
0349: * Calculates the preferred size dimensions for the specified
0350: * container, given the components it contains.
0351: *
0352: * @param parent The container to be laid out
0353: * @return The preferred size for the container
0354: * @see #maximumLayoutSize
0355: * @see #minimumLayoutSize
0356: */
0357: public Dimension preferredLayoutSize(Container parent) {
0358: synchronized (parent.getTreeLock()) {
0359: List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
0360: Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
0361: Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
0362: Dimension gridSize = buildGrid(parent, gridRows, colspans,
0363: rowspans);
0364: return getSize(parent, LayoutSize.PREFERRED, false,
0365: gridSize, gridRows, colspans, rowspans,
0366: new int[0][0]);
0367: }
0368: }
0369:
0370: /**
0371: * Calculates the maximum size dimensions for the specified
0372: * container, given the components it contains.
0373: *
0374: * @param parent The container to be laid out
0375: * @return The maximum size for the container
0376: * @see #minimumLayoutSize
0377: * @see #preferredLayoutSize
0378: */
0379: public Dimension maximumLayoutSize(Container parent) {
0380: synchronized (parent.getTreeLock()) {
0381: List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
0382: Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
0383: Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
0384: Dimension gridSize = buildGrid(parent, gridRows, colspans,
0385: rowspans);
0386: return getSize(parent, LayoutSize.MAXIMUM, false, gridSize,
0387: gridRows, colspans, rowspans, new int[0][0]);
0388: }
0389: }
0390:
0391: /**
0392: * Invalidates the layout, indicating that if the layout manager
0393: * has cached information it should be discarded.
0394: *
0395: * @param container The container for which the cached information should be discarded
0396: */
0397: public void invalidateLayout(Container container) {
0398: }
0399:
0400: /**
0401: * Lays out the specified container.
0402: *
0403: * @param parent The container to be laid out
0404: */
0405: public void layoutContainer(Container parent) {
0406: synchronized (parent.getTreeLock()) {
0407: // Pass 1: build the grid
0408: List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
0409: Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
0410: Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
0411: Dimension gridSize = buildGrid(parent, gridRows, colspans,
0412: rowspans);
0413:
0414: // Pass 2: compute minimum, preferred and maximum column widths / row heights
0415: int[][] layoutSizes = new int[6][];
0416: Dimension preferredSize = getSize(parent,
0417: LayoutSize.PREFERRED, true, gridSize, gridRows,
0418: colspans, rowspans, layoutSizes);
0419: int[] minimumColWidths = layoutSizes[0];
0420: int[] minimumRowHeights = layoutSizes[1];
0421: int[] preferredColWidths = layoutSizes[2];
0422: int[] preferredRowHeights = layoutSizes[3];
0423: int[] maximumColWidths = layoutSizes[4];
0424: int[] maximumRowHeights = layoutSizes[5];
0425:
0426: // Pass 3: redistribute free space
0427: Dimension parentSize = parent.getSize();
0428: Insets insets = parent.getInsets();
0429: int freeWidth = parentSize.width - insets.left
0430: - insets.right - (gridSize.width - 1) * hgap
0431: - distanceToBorders.left - distanceToBorders.right;
0432: int freeHeight = parentSize.height - insets.top
0433: - insets.bottom - (gridSize.height - 1) * vgap
0434: - distanceToBorders.top - distanceToBorders.bottom;
0435: redistributeSpace(preferredSize.width, freeWidth, 0,
0436: gridSize.width, preferredColWidths,
0437: minimumColWidths, maximumColWidths);
0438: redistributeSpace(preferredSize.height, freeHeight, 0,
0439: gridSize.height, preferredRowHeights,
0440: minimumRowHeights, maximumRowHeights);
0441:
0442: // Pass 4: layout components
0443: for (int row = 0, y = insets.top + distanceToBorders.top; row < gridSize.height; y += preferredRowHeights[row]
0444: + vgap, row++) {
0445: List<ExtendedGridLayoutConstraints> gridRow = gridRows
0446: .get(row);
0447: for (int col = 0, x = insets.left
0448: + distanceToBorders.left; col < gridSize.width; x += preferredColWidths[col]
0449: + hgap, col++) {
0450: ExtendedGridLayoutConstraints cell = gridRow
0451: .get(col);
0452: if ((null != cell) && (null != cell.getComponent())
0453: && !cell.isPlaceholder()) {
0454: Component component = cell.getComponent();
0455: Dimension maxSize = component.getMaximumSize();
0456: int fromCol = cell.getCol();
0457: int colspan = cell.getEffectiveColspan();
0458: int toCol = fromCol + colspan;
0459: int width = 0;
0460: for (int col2 = fromCol; col2 < toCol; col2++) {
0461: width += preferredColWidths[col2];
0462: }
0463: width += (colspan - 1) * hgap;
0464: int fromRow = cell.getRow();
0465: int rowspan = cell.getEffectiveRowspan();
0466: int toRow = fromRow + rowspan;
0467: int height = 0;
0468: for (int row2 = fromRow; row2 < toRow; row2++) {
0469: height += preferredRowHeights[row2];
0470: }
0471: height += (rowspan - 1) * vgap;
0472: int xCorrection = 0;
0473: int yCorrection = 0;
0474: if (width > maxSize.width) {
0475: xCorrection = (int) ((width - maxSize.width) * component
0476: .getAlignmentX());
0477: width = maxSize.width;
0478: }
0479: if (height > maxSize.height) {
0480: yCorrection = (int) ((height - maxSize.height) * component
0481: .getAlignmentY());
0482: height = maxSize.height;
0483: }
0484:
0485: component.setBounds(x + xCorrection, y
0486: + yCorrection, width, height);
0487: }
0488: }
0489: }
0490: }
0491: }
0492:
0493: /**
0494: * Redistributs free space (positive or negative) to all available
0495: * columns or rows while taking elements maximum and minimum sizes into
0496: * account if possible.
0497: *
0498: * @param totalSize The cumulated preferred sizes of the components
0499: * @param freeSize The available space for displaying components
0500: * without any gaps between components or between
0501: * the grid and the borders of the parent container
0502: * @param start The start in the arrays of rows or columns inclusive
0503: * @param stop The stop in the arrays of rows or columns exclusive
0504: * @param preferredElementSizes The preferredSizes of the rows or columns.
0505: * After invocation of this method, this array
0506: * holds the sizes that should be used
0507: * @param minimumElementSizes The minimumSizes of the rows or columns
0508: * @param maximumElementSizes The maximumSizes of the rows or columns
0509: */
0510: private void redistributeSpace(int totalSize, int freeSize,
0511: int start, int stop, int[] preferredElementSizes,
0512: int[] minimumElementSizes, int[] maximumElementSizes) {
0513: if (totalSize != freeSize) {
0514: boolean grow = totalSize < freeSize;
0515: // calculate the size that is available for redistribution
0516: freeSize = (freeSize - totalSize) * (grow ? 1 : -1);
0517: while (freeSize > 0) {
0518: // calculate the amount of elements that can be resized without violating
0519: // the minimum and maximum sizes and their current cumulated size
0520: int modifyableAmount = 0;
0521: long modifySize = 0;
0522: for (int i = start; i < stop; i++) {
0523: if ((grow && (preferredElementSizes[i] < maximumElementSizes[i]))
0524: || (!grow && (preferredElementSizes[i] > minimumElementSizes[i]))) {
0525: modifyableAmount++;
0526: modifySize += preferredElementSizes[i];
0527: }
0528: }
0529: boolean checkBounds = true;
0530: // if all elements are at their minimum or maximum size, resize all elements
0531: if (0 == modifyableAmount) {
0532: for (int i = start; i < stop; i++) {
0533: modifySize += preferredElementSizes[i];
0534: }
0535: checkBounds = false;
0536: modifyableAmount = stop - start;
0537: }
0538: // to prevent an endless loop if the container gets resized to a very small amount
0539: if (modifySize == 0) {
0540: break;
0541: }
0542: // resize the elements
0543: if (freeSize < modifyableAmount) {
0544: for (int i = start; i < stop; i++) {
0545: if ((freeSize != 0)
0546: && (!checkBounds || (checkBounds
0547: && (grow && (preferredElementSizes[i] < maximumElementSizes[i])) || (!grow && (preferredElementSizes[i] > minimumElementSizes[i]))))) {
0548: preferredElementSizes[i] += (grow ? 1 : -1);
0549: if (0 > preferredElementSizes[i]) {
0550: preferredElementSizes[i] = 0;
0551: }
0552: freeSize--;
0553: }
0554: }
0555: } else {
0556: long modifySizeAddition = 0;
0557: double factor = (double) (freeSize + modifySize)
0558: / (double) modifySize;
0559: for (int i = start; i < stop; i++) {
0560: long modifyableSize = (checkBounds ? (grow ? maximumElementSizes[i]
0561: - preferredElementSizes[i]
0562: : preferredElementSizes[i]
0563: - minimumElementSizes[i])
0564: : Integer.MAX_VALUE
0565: - preferredElementSizes[i]);
0566: long elementModifySize = Math
0567: .abs(Math
0568: .round((factor * preferredElementSizes[i])
0569: - preferredElementSizes[i]));
0570: if (elementModifySize <= modifyableSize) {
0571: preferredElementSizes[i] += (grow ? elementModifySize
0572: : -elementModifySize);
0573: modifySizeAddition += (grow ? elementModifySize
0574: : -elementModifySize);
0575: freeSize -= elementModifySize;
0576: } else {
0577: preferredElementSizes[i] += (grow ? modifyableSize
0578: : -modifyableSize);
0579: modifySizeAddition += (grow ? modifyableSize
0580: : -modifyableSize);
0581: freeSize -= modifyableSize;
0582: }
0583: if (0 > preferredElementSizes[i]) {
0584: preferredElementSizes[i] = 0;
0585: }
0586: }
0587: modifySize += modifySizeAddition;
0588: }
0589: }
0590: }
0591: }
0592:
0593: /**
0594: * Calculates the minimum, preferred or maximum size dimensions
0595: * for the specified container, given the components it contains.
0596: *
0597: * @param parent The container to be laid out
0598: * @param layoutSize if {@code LayoutSize.MINIMUM} compute minimum layout size,
0599: * if {@code LayoutSize.PREFERRED} compute preferred layout size,
0600: * if {@code LayoutSize.MAXIMUM} compute maximum layout size,
0601: * if {@code fillRawSizes} is {@code true}, the layout size is computed
0602: * without applying gaps between components or between
0603: * the grid and the borders of the parent container
0604: * @param fillRawSizes Whether to fill the resultArrays with the raw
0605: * row heights and column widths and whether to apply
0606: * gaps between components or between
0607: * the grid and the borders of the parent container
0608: * when computing the layout size
0609: * @param gridSize The amount of rows and columns in the grid
0610: * @param gridRows The grid holding the constraints for the components
0611: * @param colspans In this {@code Set} the constraints which are part
0612: * of a colspan are stored
0613: * @param rowspans In this {@code Set} the constraints which are part
0614: * of a rowspan are stored
0615: * @param resultArrays If {@code fillRawSizes} is {@code true}, the first six arrays
0616: * get filled with the raw row heights and column widths.
0617: * resultArrays[0] = resultMinimumColWidths;
0618: * resultArrays[1] = resultMinimumRowHeights;
0619: * resultArrays[2] = resultPreferredColWidths;
0620: * resultArrays[3] = resultPreferredRowHeights;
0621: * resultArrays[4] = resultMaximumColWidths;
0622: * resultArrays[5] = resultMaximumRowHeights;
0623: * @return The minimum, preferred or maximum size dimensions for the specified container
0624: * @throws IllegalArgumentException If {@code fillRawSizes == true} and {@code resultArrays.length < 6}
0625: */
0626: private Dimension getSize(Container parent, LayoutSize layoutSize,
0627: boolean fillRawSizes, Dimension gridSize,
0628: List<List<ExtendedGridLayoutConstraints>> gridRows,
0629: Set<ExtendedGridLayoutConstraints> colspans,
0630: Set<ExtendedGridLayoutConstraints> rowspans,
0631: int[][] resultArrays) {
0632: if (fillRawSizes && (resultArrays.length < 6)) {
0633: throw new IllegalArgumentException(
0634: "If fillRawSizes is true, resultArrays.length must be >= 6 ("
0635: + resultArrays.length + ')');
0636: }
0637: int[] minimumColWidths = new int[gridSize.width];
0638: int[] minimumRowHeights = new int[gridSize.height];
0639: int[] preferredColWidths = new int[gridSize.width];
0640: int[] preferredRowHeights = new int[gridSize.height];
0641: int[] maximumColWidths = new int[gridSize.width];
0642: int[] maximumRowHeights = new int[gridSize.height];
0643: Arrays.fill(minimumColWidths, 0);
0644: Arrays.fill(minimumRowHeights, 0);
0645: Arrays.fill(preferredColWidths, 0);
0646: Arrays.fill(preferredRowHeights, 0);
0647: Arrays.fill(maximumColWidths, 0);
0648: Arrays.fill(maximumRowHeights, 0);
0649:
0650: // get the maximum of the minimum sizes,
0651: // the maximum of the preferred sizes and
0652: // the minimum of the maximum sizes
0653: // of all rows and columns, not taking
0654: // rowspans and colspans into account
0655: for (int row = 0; row < gridSize.height; row++) {
0656: List<ExtendedGridLayoutConstraints> gridRow = gridRows
0657: .get(row);
0658: for (int col = 0; col < gridSize.width; col++) {
0659: ExtendedGridLayoutConstraints cell = gridRow.get(col);
0660: if ((null != cell) && (null != cell.getComponent())) {
0661: Component component = cell.getComponent();
0662: Dimension minimumSize = component.getMinimumSize();
0663: Dimension preferredSize = component
0664: .getPreferredSize();
0665: Dimension maximumSize = component.getMaximumSize();
0666: if (!colspans.contains(cell)) {
0667: minimumColWidths[col] = Math.max(
0668: minimumColWidths[col],
0669: minimumSize.width);
0670: preferredColWidths[col] = Math.max(
0671: preferredColWidths[col],
0672: preferredSize.width);
0673: maximumColWidths[col] = Math.max(
0674: maximumColWidths[col],
0675: maximumSize.width);
0676: }
0677: if (!rowspans.contains(cell)) {
0678: minimumRowHeights[row] = Math.max(
0679: minimumRowHeights[row],
0680: minimumSize.height);
0681: preferredRowHeights[row] = Math.max(
0682: preferredRowHeights[row],
0683: preferredSize.height);
0684: maximumRowHeights[row] = Math.max(
0685: maximumRowHeights[row],
0686: maximumSize.height);
0687: }
0688: }
0689: }
0690: }
0691:
0692: // correct cases where
0693: // minimumColWidths[col] <= preferredColWidths[col] <= maximumColWidths[col]
0694: // is not true by clipping to the minimumColWidths and maximumColWidths
0695: for (int col = 0; col < gridSize.width; col++) {
0696: if (minimumColWidths[col] >= maximumColWidths[col]) {
0697: maximumColWidths[col] = minimumColWidths[col];
0698: preferredColWidths[col] = minimumColWidths[col];
0699: } else if (preferredColWidths[col] < minimumColWidths[col]) {
0700: preferredColWidths[col] = minimumColWidths[col];
0701: } else if (preferredColWidths[col] > maximumColWidths[col]) {
0702: preferredColWidths[col] = maximumColWidths[col];
0703: }
0704: }
0705:
0706: // plug in the colspans and correct the minimum, preferred and
0707: // maximum column widths the colspans are part of
0708: for (ExtendedGridLayoutConstraints cell : colspans) {
0709: int fromCol = cell.getCol();
0710: int colspan = cell.getEffectiveColspan();
0711: int toCol = fromCol + colspan;
0712: int currentMinimumColWidth = 0;
0713: int currentPreferredColWidth = 0;
0714: int currentMaximumColWidth = 0;
0715: for (int col = fromCol; col < toCol; col++) {
0716: int minimumColWidth = minimumColWidths[col];
0717: if ((Integer.MAX_VALUE - minimumColWidth) < currentMinimumColWidth) {
0718: currentMinimumColWidth = Integer.MAX_VALUE;
0719: } else {
0720: currentMinimumColWidth += minimumColWidth;
0721: }
0722: int preferredColWidth = preferredColWidths[col];
0723: if ((Integer.MAX_VALUE - preferredColWidth) < currentPreferredColWidth) {
0724: currentPreferredColWidth = Integer.MAX_VALUE;
0725: } else {
0726: currentPreferredColWidth += preferredColWidth;
0727: }
0728: int maximumColWidth = maximumColWidths[col];
0729: if ((Integer.MAX_VALUE - maximumColWidth) < currentMaximumColWidth) {
0730: currentMaximumColWidth = Integer.MAX_VALUE;
0731: } else {
0732: currentMaximumColWidth += maximumColWidth;
0733: }
0734: }
0735: Component component = cell.getComponent();
0736: int wantedMaximumColWidth = component.getMaximumSize().width
0737: - ((colspan - 1) * hgap);
0738: if (currentMaximumColWidth < wantedMaximumColWidth) {
0739: redistributeSpace(currentMaximumColWidth,
0740: wantedMaximumColWidth, fromCol, toCol,
0741: maximumColWidths, maximumColWidths,
0742: maximumColWidths);
0743: }
0744: int wantedMinimumColWidth = component.getMinimumSize().width
0745: - ((colspan - 1) * hgap);
0746: if (currentMinimumColWidth < wantedMinimumColWidth) {
0747: redistributeSpace(currentMinimumColWidth,
0748: wantedMinimumColWidth, fromCol, toCol,
0749: minimumColWidths, minimumColWidths,
0750: maximumColWidths);
0751: }
0752: int wantedPreferredColWidth = component.getPreferredSize().width
0753: - ((colspan - 1) * hgap);
0754: if (currentPreferredColWidth < wantedPreferredColWidth) {
0755: redistributeSpace(currentPreferredColWidth,
0756: wantedPreferredColWidth, fromCol, toCol,
0757: preferredColWidths, minimumColWidths,
0758: maximumColWidths);
0759: }
0760: }
0761:
0762: // correct cases where
0763: // minimumColWidths[col] <= preferredColWidths[col] <= maximumColWidths[col]
0764: // is not true by clipping to the minimumColWidths and maximumColWidths
0765: for (int col = 0; col < gridSize.width; col++) {
0766: if (minimumColWidths[col] >= maximumColWidths[col]) {
0767: maximumColWidths[col] = minimumColWidths[col];
0768: preferredColWidths[col] = minimumColWidths[col];
0769: } else if (preferredColWidths[col] < minimumColWidths[col]) {
0770: preferredColWidths[col] = minimumColWidths[col];
0771: } else if (preferredColWidths[col] > maximumColWidths[col]) {
0772: preferredColWidths[col] = maximumColWidths[col];
0773: }
0774: }
0775:
0776: // correct cases where
0777: // minimumRowHeights[row] <= preferredRowHeights[row] <= maximumRowHeights[row]
0778: // is not true by clipping to the minimumRowHeights and maximumRowHeights
0779: for (int row = 0; row < gridSize.height; row++) {
0780: if (minimumRowHeights[row] >= maximumRowHeights[row]) {
0781: maximumRowHeights[row] = minimumRowHeights[row];
0782: preferredRowHeights[row] = minimumRowHeights[row];
0783: } else if (preferredRowHeights[row] < minimumRowHeights[row]) {
0784: preferredRowHeights[row] = minimumRowHeights[row];
0785: } else if (preferredRowHeights[row] > maximumRowHeights[row]) {
0786: preferredRowHeights[row] = maximumRowHeights[row];
0787: }
0788: }
0789:
0790: // plug in the rowspans and correct the minimum, preferred and
0791: // maximum row heights the rowspans are part of
0792: for (ExtendedGridLayoutConstraints cell : rowspans) {
0793: int fromRow = cell.getRow();
0794: int rowspan = cell.getEffectiveRowspan();
0795: int toRow = fromRow + rowspan;
0796: int currentMinimumRowHeight = 0;
0797: int currentPreferredRowHeight = 0;
0798: int currentMaximumRowHeight = 0;
0799: for (int row = fromRow; row < toRow; row++) {
0800: int minimumRowHeight = minimumRowHeights[row];
0801: if ((Integer.MAX_VALUE - minimumRowHeight) < currentMinimumRowHeight) {
0802: currentMinimumRowHeight = Integer.MAX_VALUE;
0803: } else {
0804: currentMinimumRowHeight += minimumRowHeight;
0805: }
0806: int preferredRowHeight = preferredRowHeights[row];
0807: if ((Integer.MAX_VALUE - preferredRowHeight) < currentPreferredRowHeight) {
0808: currentPreferredRowHeight = Integer.MAX_VALUE;
0809: } else {
0810: currentPreferredRowHeight += preferredRowHeight;
0811: }
0812: int maximumRowHeight = maximumRowHeights[row];
0813: if ((Integer.MAX_VALUE - maximumRowHeight) < currentMaximumRowHeight) {
0814: currentMaximumRowHeight = Integer.MAX_VALUE;
0815: } else {
0816: currentMaximumRowHeight += maximumRowHeight;
0817: }
0818: }
0819: Component component = cell.getComponent();
0820: int wantedMaximumRowHeight = component.getMaximumSize().height
0821: - ((rowspan - 1) * vgap);
0822: if (currentMaximumRowHeight < wantedMaximumRowHeight) {
0823: redistributeSpace(currentMaximumRowHeight,
0824: wantedMaximumRowHeight, fromRow, toRow,
0825: maximumRowHeights, maximumRowHeights,
0826: maximumRowHeights);
0827: }
0828: int wantedMinimumRowHeight = component.getMinimumSize().height
0829: - ((rowspan - 1) * vgap);
0830: if (currentMinimumRowHeight < wantedMinimumRowHeight) {
0831: redistributeSpace(currentMinimumRowHeight,
0832: wantedMinimumRowHeight, fromRow, toRow,
0833: minimumRowHeights, minimumRowHeights,
0834: maximumRowHeights);
0835: }
0836: int wantedPreferredRowHeight = component.getPreferredSize().height
0837: - ((rowspan - 1) * vgap);
0838: if (currentPreferredRowHeight < wantedPreferredRowHeight) {
0839: redistributeSpace(currentPreferredRowHeight,
0840: wantedPreferredRowHeight, fromRow, toRow,
0841: preferredRowHeights, minimumRowHeights,
0842: maximumRowHeights);
0843: }
0844: }
0845:
0846: // correct cases where
0847: // minimumRowHeights[row] <= preferredRowHeights[row] <= maximumRowHeights[row]
0848: // is not true by clipping to the minimumRowHeights and maximumRowHeights
0849: for (int row = 0; row < gridSize.height; row++) {
0850: if (minimumRowHeights[row] >= maximumRowHeights[row]) {
0851: maximumRowHeights[row] = minimumRowHeights[row];
0852: preferredRowHeights[row] = minimumRowHeights[row];
0853: } else if (preferredRowHeights[row] < minimumRowHeights[row]) {
0854: preferredRowHeights[row] = minimumRowHeights[row];
0855: } else if (preferredRowHeights[row] > maximumRowHeights[row]) {
0856: preferredRowHeights[row] = maximumRowHeights[row];
0857: }
0858: }
0859:
0860: // copies the computed sizes to the result arrays
0861: if (fillRawSizes) {
0862: resultArrays[0] = minimumColWidths;
0863: resultArrays[1] = minimumRowHeights;
0864: resultArrays[2] = preferredColWidths;
0865: resultArrays[3] = preferredRowHeights;
0866: resultArrays[4] = maximumColWidths;
0867: resultArrays[5] = maximumRowHeights;
0868: }
0869:
0870: // sums up the sizes for return value
0871: int[] colWidths;
0872: int[] rowHeights;
0873: switch (layoutSize) {
0874: case MINIMUM:
0875: colWidths = minimumColWidths;
0876: rowHeights = minimumRowHeights;
0877: break;
0878:
0879: case PREFERRED:
0880: colWidths = preferredColWidths;
0881: rowHeights = preferredRowHeights;
0882: break;
0883:
0884: case MAXIMUM:
0885: colWidths = maximumColWidths;
0886: rowHeights = maximumRowHeights;
0887: break;
0888:
0889: default:
0890: throw new InternalError(
0891: "Missing case branch for LayoutSize: " + layoutSize);
0892: }
0893: long totalWidth = 0;
0894: long totalHeight = 0;
0895: for (int width : colWidths) {
0896: totalWidth += width;
0897: }
0898: for (int height : rowHeights) {
0899: totalHeight += height;
0900: }
0901:
0902: // add space between components or between
0903: // componetns and the borders of the parent container
0904: if (!fillRawSizes) {
0905: Insets insets = parent.getInsets();
0906: totalWidth += insets.left + insets.right
0907: + ((gridSize.width - 1) * hgap)
0908: + distanceToBorders.left + distanceToBorders.right;
0909: totalHeight += insets.top + insets.bottom
0910: + ((gridSize.height - 1) * vgap)
0911: + distanceToBorders.top + distanceToBorders.bottom;
0912: }
0913:
0914: // clip the size to Integer.MAX_VALUE if too big
0915: if (totalWidth > Integer.MAX_VALUE) {
0916: totalWidth = Integer.MAX_VALUE;
0917: }
0918: if (totalHeight > Integer.MAX_VALUE) {
0919: totalHeight = Integer.MAX_VALUE;
0920: }
0921:
0922: return new Dimension((int) totalWidth, (int) totalHeight);
0923: }
0924:
0925: /**
0926: * Builds up the grid for the specified container,
0927: * given the components it contains.
0928: *
0929: * @param parent The container to be laid out
0930: * @param gridRows In this {@code List<List>} the grid gets stored
0931: * @param colspans In this {@code Set} the constraints which are part
0932: * of a colspan get stored
0933: * @param rowspans In this {@code Set} the constraints which are part
0934: * of a rowspan get stored
0935: * @return The amount of rows and columns in the grid
0936: */
0937: private Dimension buildGrid(Container parent,
0938: List<List<ExtendedGridLayoutConstraints>> gridRows,
0939: Set<ExtendedGridLayoutConstraints> colspans,
0940: Set<ExtendedGridLayoutConstraints> rowspans) {
0941: // put the parent's components in source rows
0942: List<List<ExtendedGridLayoutConstraints>> rows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
0943: Component[] components = parent.getComponents();
0944: for (Component component : components) {
0945: if (component.isVisible()) {
0946: ExtendedGridLayoutConstraints constraints = lookupConstraints(
0947: component).getWorkCopy();
0948: int rowNumber = constraints.getRow();
0949: for (int i = rowNumber, c = rows.size(); i >= c; i--) {
0950: rows
0951: .add(new ArrayList<ExtendedGridLayoutConstraints>());
0952: }
0953: List<ExtendedGridLayoutConstraints> row = rows
0954: .get(rowNumber);
0955: row.add(constraints);
0956: }
0957: }
0958:
0959: // initialize the rowIterators, gridRowIterators and gridRows
0960: List<Iterator<ExtendedGridLayoutConstraints>> rowIterators = new ArrayList<Iterator<ExtendedGridLayoutConstraints>>();
0961: List<ListIterator<ExtendedGridLayoutConstraints>> gridRowIterators = new ArrayList<ListIterator<ExtendedGridLayoutConstraints>>();
0962: boolean haveNext = false;
0963: for (List<ExtendedGridLayoutConstraints> row : rows) {
0964: Iterator<ExtendedGridLayoutConstraints> rowIterator = row
0965: .iterator();
0966: rowIterators.add(rowIterator);
0967: if (rowIterator.hasNext()) {
0968: haveNext = true;
0969: }
0970: List<ExtendedGridLayoutConstraints> gridRow = new ArrayList<ExtendedGridLayoutConstraints>();
0971: gridRows.add(gridRow);
0972: gridRowIterators.add(gridRow.listIterator());
0973: }
0974:
0975: // build the grid
0976: int col = -1;
0977: while (haveNext) {
0978: col++;
0979: haveNext = false;
0980: for (int row = 0, c = gridRows.size(); row < c; row++) {
0981: Iterator<ExtendedGridLayoutConstraints> rowIterator = rowIterators
0982: .get(row);
0983: ListIterator<ExtendedGridLayoutConstraints> gridRowIterator = gridRowIterators
0984: .get(row);
0985:
0986: // look for a rowspan in the previous row
0987: if (row > 0) {
0988: ExtendedGridLayoutConstraints rowspanSource = gridRows
0989: .get(row - 1).get(col);
0990: if (null != rowspanSource) {
0991: ExtendedGridLayoutConstraints rowspanPlaceholder = rowspanSource
0992: .getRowspanPlaceholder(true);
0993: if (null != rowspanPlaceholder) {
0994: rowspans.add(rowspanSource);
0995: gridRowIterator.add(rowspanPlaceholder);
0996: if (null != rowspanPlaceholder
0997: .getColspanPlaceholder(false)) {
0998: switch (rowspanPlaceholder.getColspan()) {
0999: case REMAINDER:
1000: break;
1001:
1002: default:
1003: haveNext = true;
1004: }
1005: } else if (rowIterator.hasNext()) {
1006: haveNext = true;
1007: }
1008: continue;
1009: }
1010: }
1011: }
1012:
1013: // look for a colspan in the previous column
1014: if (gridRowIterator.hasPrevious()) {
1015: ExtendedGridLayoutConstraints colspanSource = gridRowIterator
1016: .previous();
1017: gridRowIterator.next();
1018: if (null != colspanSource) {
1019: ExtendedGridLayoutConstraints colspanPlaceholder = colspanSource
1020: .getColspanPlaceholder(true);
1021: if (null != colspanPlaceholder) {
1022: colspans.add(colspanSource);
1023: gridRowIterator.add(colspanPlaceholder);
1024: if (null != colspanPlaceholder
1025: .getColspanPlaceholder(false)) {
1026: switch (colspanPlaceholder.getColspan()) {
1027: case REMAINDER:
1028: break;
1029:
1030: default:
1031: haveNext = true;
1032: }
1033: } else if (rowIterator.hasNext()) {
1034: haveNext = true;
1035: }
1036: continue;
1037: }
1038: }
1039: }
1040:
1041: // add a new element or null
1042: if (rowIterator.hasNext()) {
1043: ExtendedGridLayoutConstraints newConstraints = rowIterator
1044: .next();
1045: newConstraints.setCol(col);
1046: gridRowIterator.add(newConstraints);
1047: if (null != newConstraints
1048: .getColspanPlaceholder(false)) {
1049: switch (newConstraints.getColspan()) {
1050: case REMAINDER:
1051: break;
1052:
1053: default:
1054: haveNext = true;
1055: }
1056: } else if (rowIterator.hasNext()) {
1057: haveNext = true;
1058: }
1059: } else {
1060: gridRowIterator.add(null);
1061: }
1062: }
1063: }
1064:
1065: // check the last gridRow for rowspans and probably add rows for these
1066: haveNext = false;
1067: int gridRowsSize = gridRows.size();
1068: if (gridRowsSize > 0) {
1069: ListIterator<ExtendedGridLayoutConstraints> gridRowIterator = gridRows
1070: .get(gridRows.size() - 1).listIterator();
1071: while (gridRowIterator.hasNext()) {
1072: ExtendedGridLayoutConstraints cell = gridRowIterator
1073: .next();
1074: if ((null != cell)
1075: && ((REMAINDER != cell.getRowspan()) && (null != cell
1076: .getRowspanPlaceholder(false)))) {
1077: haveNext = true;
1078: break;
1079: }
1080: }
1081: while (haveNext) {
1082: haveNext = false;
1083: gridRowIterator = gridRows.get(gridRows.size() - 1)
1084: .listIterator();
1085: List<ExtendedGridLayoutConstraints> gridRow = new ArrayList<ExtendedGridLayoutConstraints>();
1086: gridRows.add(gridRow);
1087: ListIterator<ExtendedGridLayoutConstraints> newGridRowIterator = gridRow
1088: .listIterator();
1089: while (gridRowIterator.hasNext()) {
1090: ExtendedGridLayoutConstraints cell = gridRowIterator
1091: .next();
1092: if ((null != cell)
1093: && (null != cell
1094: .getRowspanPlaceholder(false))) {
1095: rowspans.add(cell);
1096: ExtendedGridLayoutConstraints rowspanPlaceholder = cell
1097: .getRowspanPlaceholder(true);
1098: newGridRowIterator.add(rowspanPlaceholder);
1099: } else {
1100: newGridRowIterator.add(null);
1101: }
1102: }
1103: gridRowIterator = gridRow.listIterator();
1104: while (gridRowIterator.hasNext()) {
1105: ExtendedGridLayoutConstraints cell = gridRowIterator
1106: .next();
1107: if ((null != cell)
1108: && ((REMAINDER != cell.getRowspan()) && (null != cell
1109: .getRowspanPlaceholder(false)))) {
1110: haveNext = true;
1111: break;
1112: }
1113: }
1114: }
1115: }
1116:
1117: return new Dimension(col + 1, gridRows.size());
1118: }
1119:
1120: /**
1121: * Returns a string representation of the object. In general, the
1122: * {@code toString} method returns a string that
1123: * "textually represents" this object. The result should
1124: * be a concise but informative representation that is easy for a
1125: * person to read.
1126: *
1127: * @return a string representation of the object.
1128: */
1129: public String toString() {
1130: return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
1131: + ",distanceToBorders=" + distanceToBorders
1132: + ",comptable=" + comptable + "]";
1133: }
1134: }
|