JAL-3026 MiGLayout-SwingJS source
authorhansonr <hansonr@stolaf.edu>
Tue, 3 Jul 2018 18:16:18 +0000 (19:16 +0100)
committerhansonr <hansonr@stolaf.edu>
Tue, 3 Jul 2018 18:16:18 +0000 (19:16 +0100)
22 files changed:
srcjar/net/miginfocom/README_SWINGJS.txt [new file with mode: 0644]
srcjar/net/miginfocom/layout/AC.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/AnimSpec.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/BoundSize.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/CC.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/ComponentWrapper.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/ConstraintParser.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/ContainerWrapper.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/DimConstraint.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/Grid.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/InCellGapProvider.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/LC.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/LayoutCallback.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/LayoutUtil.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/LinkHandler.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/PlatformDefaults.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/ResizeConstraint.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/UnitConverter.java [new file with mode: 0644]
srcjar/net/miginfocom/layout/UnitValue.java [new file with mode: 0644]
srcjar/net/miginfocom/swing/MigLayout.java [new file with mode: 0644]
srcjar/net/miginfocom/swing/SwingComponentWrapper.java [new file with mode: 0644]
srcjar/net/miginfocom/swing/SwingContainerWrapper.java [new file with mode: 0644]

diff --git a/srcjar/net/miginfocom/README_SWINGJS.txt b/srcjar/net/miginfocom/README_SWINGJS.txt
new file mode 100644 (file)
index 0000000..cec8076
--- /dev/null
@@ -0,0 +1,10 @@
+https://github.com/mikaelgrev/miglayout.git
+2018.07.03 BH Latest commit f2a231c
+
+Status
+======
+
+7/3/2018
+
+no changes necessary
diff --git a/srcjar/net/miginfocom/layout/AC.java b/srcjar/net/miginfocom/layout/AC.java
new file mode 100644 (file)
index 0000000..2d1032c
--- /dev/null
@@ -0,0 +1,567 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A constraint that holds the column <b>or</b> row constraints for the grid. It also holds the gaps between the rows and columns.
+ * <p>
+ * This class is a holder and builder for a number of {@link net.miginfocom.layout.DimConstraint}s.
+ * <p>
+ * For a more thorough explanation of what these constraints do, and how to build the constraints, see the White Paper or Cheat Sheet at www.migcomponents.com.
+ * <p>
+ * Note that there are two way to build this constraint. Through String (e.g. <code>"[100]3[200,fill]"</code> or through API (E.g.
+ * <code>new AC().size("100").gap("3").size("200").fill()</code>.
+ */
+public final class AC implements Externalizable
+{
+       private final ArrayList<DimConstraint> cList = new ArrayList<DimConstraint>(1);
+
+       private transient int curIx = 0;
+
+       /** Constructor. Creates an instance that can be configured manually. Will be initialized with a default
+        * {@link net.miginfocom.layout.DimConstraint}.
+        */
+       public AC()
+       {
+               cList.add(new DimConstraint());
+       }
+
+       /** Property. The different {@link net.miginfocom.layout.DimConstraint}s that this object consists of.
+        * These <code>DimConstraints</code> contains all information in this class.
+        * <p>
+        * Yes, we are embarrassingly aware that the method is misspelled.
+        * @return The different {@link net.miginfocom.layout.DimConstraint}s that this object consists of. A new list and
+        * never <code>null</code>.
+        */
+       public final DimConstraint[] getConstaints()
+       {
+               return cList.toArray(new DimConstraint[cList.size()]);
+       }
+
+       /** Sets the different {@link net.miginfocom.layout.DimConstraint}s that this object should consists of.
+        * <p>
+        * Yes, we are embarrassingly aware that the method is misspelled.
+        * @param constr The different {@link net.miginfocom.layout.DimConstraint}s that this object consists of. The list
+        * will be copied for storage. <code>null</code> or and empty array will reset the constraints to one <code>DimConstraint</code>
+        * with default values.
+        */
+       public final void setConstaints(DimConstraint[] constr)
+       {
+               if (constr == null || constr.length < 1 )
+                       constr = new DimConstraint[] {new DimConstraint()};
+
+               cList.clear();
+               cList.ensureCapacity(constr.length);
+               for (DimConstraint c : constr)
+                       cList.add(c);
+       }
+
+       /** Returns the number of rows/columns that this constraints currently have.
+        * @return The number of rows/columns that this constraints currently have. At least 1.
+        */
+       public int getCount()
+       {
+               return cList.size();
+       }
+
+       /** Sets the total number of rows/columns to <code>size</code>. If the number of rows/columns is already more
+        * than <code>size</code> nothing will happen.
+        * @param size The total number of rows/columns
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC count(int size)
+       {
+               makeSize(size);
+               return this;
+       }
+
+       /** Specifies that the current row/column should not be grid-like. The while row/column will have its components layed out
+        * in one single cell. It is the same as to say that the cells in this column/row will all be merged (a.k.a spanned).
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC noGrid()
+       {
+               return noGrid(curIx);
+       }
+
+       /** Specifies that the indicated rows/columns should not be grid-like. The while row/column will have its components layed out
+        * in one single cell. It is the same as to say that the cells in this column/row will all be merged (a.k.a spanned).
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC noGrid(int... indexes)
+       {
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setNoGrid(true);
+               }
+               return this;
+       }
+
+       /** Sets the current row/column to <code>i</code>. If the current number of rows/columns is less than <code>i</code> a call
+        * to {@link #count(int)} will set the size accordingly.
+        * <p>
+        * The next call to any of the constraint methods (e.g. {@link net.miginfocom.layout.AC#noGrid}) will be carried
+        * out on this new row/column.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param i The new current row/column.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC index(int i)
+       {
+               makeSize(i);
+               curIx = i;
+               return this;
+       }
+
+       /** Specifies that the current row/column's component should grow by default. It does not affect the size of the row/column.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC fill()
+       {
+               return fill(curIx);
+       }
+
+       /** Specifies that the indicated rows'/columns' component should grow by default. It does not affect the size of the row/column.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC fill(int... indexes)
+       {
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setFill(true);
+               }
+               return this;
+       }
+
+//     /** Specifies that the current row/column should be put in the end group <code>s</code> and will thus share the same ending
+//      * coordinate within the group.
+//      * <p>
+//      * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+//      * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+//      * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+//      */
+//     public final AxisConstraint endGroup(String s)
+//     {
+//             return endGroup(s, curIx);
+//     }
+//
+//     /** Specifies that the indicated rows/columns should be put in the end group <code>s</code> and will thus share the same ending
+//      * coordinate within the group.
+//      * <p>
+//      * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+//      * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+//      * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+//      * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+//      */
+//     public final AxisConstraint endGroup(String s, int... indexes)
+//     {
+//             for (int i = indexes.length - 1; i >= 0; i--) {
+//                     int ix = indexes[i];
+//                     makeSize(ix);
+//                     cList.get(ix).setEndGroup(s);
+//             }
+//             return this;
+//     }
+
+       /** Specifies that the current row/column should be put in the size group <code>s</code> and will thus share the same size
+        * constraints as the other components in the group.
+        * <p>
+        * Same as <code>sizeGroup("")</code>
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final AC sizeGroup()
+       {
+               return sizeGroup("", curIx);
+       }
+
+       /** Specifies that the current row/column should be put in the size group <code>s</code> and will thus share the same size
+        * constraints as the other components in the group.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC sizeGroup(String s)
+       {
+               return sizeGroup(s, curIx);
+       }
+
+       /** Specifies that the indicated rows/columns should be put in the size group <code>s</code> and will thus share the same size
+        * constraints as the other components in the group.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC sizeGroup(String s, int... indexes)
+       {
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setSizeGroup(s);
+               }
+               return this;
+       }
+
+       /** Specifies the current row/column's min and/or preferred and/or max size. E.g. <code>"10px"</code> or <code>"50:100:200"</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The minimum and/or preferred and/or maximum size of this row. The string will be interpreted
+        * as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC size(String s)
+       {
+               return size(s, curIx);
+       }
+
+       /** Specifies the indicated rows'/columns' min and/or preferred and/or max size. E.g. <code>"10px"</code> or <code>"50:100:200"</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The minimum and/or preferred and/or maximum size of this row. The string will be interpreted
+        * as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC size(String size, int... indexes)
+       {
+               BoundSize bs = ConstraintParser.parseBoundSize(size, false, true);
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setSize(bs);
+               }
+               return this;
+       }
+
+       /** Specifies the gap size to be the default one <b>AND</b> moves to the next column/row. The method is called <code>.gap()</code>
+        * rather the more natural <code>.next()</code> to indicate that it is very much related to the other <code>.gap(..)</code> methods.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC gap()
+       {
+               curIx++;
+               makeSize(curIx);
+               return this;
+       }
+
+       /** Specifies the gap size to <code>size</code> <b>AND</b> moves to the next column/row.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size minimum and/or preferred and/or maximum size of the gap between this and the next row/column.
+        * The string will be interpreted as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC gap(String size)
+       {
+               return gap(size, curIx++);
+       }
+
+       /** Specifies the indicated rows'/columns' gap size to <code>size</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size minimum and/or preferred and/or maximum size of the gap between this and the next row/column.
+        * The string will be interpreted as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC gap(String size, int... indexes)
+       {
+               BoundSize bsa = size != null ? ConstraintParser.parseBoundSize(size, true, true) : null;
+
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix + 1);
+                       if (bsa != null)
+                               cList.get(ix).setGapAfter(bsa);
+               }
+               return this;
+       }
+
+       /** Specifies the current row/column's columns default alignment <b>for its components</b>. It does not affect the positioning
+        * or size of the columns/row itself. For columns it is the horizontal alignment (e.g. "left") and for rows it is the vertical
+        * alignment (e.g. "top").
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param side The default side to align the components. E.g. "top" or "left", or "leading" or "trailing" or "bottom" or "right".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC align(String side)
+       {
+               return align(side, curIx);
+       }
+
+       /** Specifies the indicated rows'/columns' columns default alignment <b>for its components</b>. It does not affect the positioning
+        * or size of the columns/row itself. For columns it is the horizontal alignment (e.g. "left") and for rows it is the vertical
+        * alignment (e.g. "top").
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param side The default side to align the components. E.g. "top" or "left", or "before" or "after" or "bottom" or "right".
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC align(String side, int... indexes)
+       {
+               UnitValue al = ConstraintParser.parseAlignKeywords(side, true);
+               if (al == null)
+                       al = ConstraintParser.parseAlignKeywords(side, false);
+
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setAlign(al);
+               }
+               return this;
+       }
+
+       /** Specifies the current row/column's grow priority.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The new grow priority.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC growPrio(int p)
+       {
+               return growPrio(p, curIx);
+       }
+
+       /** Specifies the indicated rows'/columns' grow priority.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The new grow priority.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC growPrio(int p, int... indexes)
+       {
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setGrowPriority(p);
+               }
+               return this;
+       }
+
+       /** Specifies the current row/column's grow weight within columns/rows with the <code>grow priority</code> 100f.
+        * <p>
+        * Same as <code>grow(100f)</code>
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final AC grow()
+       {
+               return grow(100f, curIx);
+       }
+
+       /** Specifies the current row/column's grow weight within columns/rows with the same <code>grow priority</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new grow weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC grow(float w)
+       {
+               return grow(w, curIx);
+       }
+
+       /** Specifies the indicated rows'/columns' grow weight within columns/rows with the same <code>grow priority</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new grow weight.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC grow(float w, int... indexes)
+       {
+               Float gw = new Float(w);
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setGrow(gw);
+               }
+               return this;
+       }
+
+       /** Specifies the current row/column's shrink priority.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The new shrink priority.
+                * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC shrinkPrio(int p)
+       {
+               return shrinkPrio(p, curIx);
+       }
+
+       /** Specifies the indicated rows'/columns' shrink priority.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The new shrink priority.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        */
+       public final AC shrinkPrio(int p, int... indexes)
+       {
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setShrinkPriority(p);
+               }
+               return this;
+       }
+
+       /** Specifies that the current row/column's shrink weight within the columns/rows with the <code>shrink priority</code> 100f.
+        * <p>
+        * Same as <code>shrink(100f)</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final AC shrink()
+       {
+               return shrink(100f, curIx);
+       }
+
+       /** Specifies that the current row/column's shrink weight within the columns/rows with the same <code>shrink priority</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+        * @param w The shrink weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final AC shrink(float w)
+       {
+               return shrink(w, curIx);
+       }
+
+       /** Specifies the indicated rows'/columns' shrink weight within the columns/rows with the same <code>shrink priority</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+        * @param w The shrink weight.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final AC shrink(float w, int... indexes)
+       {
+               Float sw = new Float(w);
+               for (int i = indexes.length - 1; i >= 0; i--) {
+                       int ix = indexes[i];
+                       makeSize(ix);
+                       cList.get(ix).setShrink(sw);
+               }
+               return this;
+       }
+
+       /** Specifies that the current row/column's shrink weight within the columns/rows with the same <code>shrink priority</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+        * @param w The shrink weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @deprecated in 3.7.2. Use {@link #shrink(float)} instead.
+        */
+       public final AC shrinkWeight(float w)
+       {
+               return shrink(w);
+       }
+
+       /** Specifies the indicated rows'/columns' shrink weight within the columns/rows with the same <code>shrink priority</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+        * @param w The shrink weight.
+        * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+        * @deprecated in 3.7.2. Use {@link #shrink(float, int...)} instead.
+        */
+       public final AC shrinkWeight(float w, int... indexes)
+       {
+               return shrink(w, indexes);
+       }
+
+       private void makeSize(int sz)
+       {
+               if (cList.size() <= sz) {
+                       cList.ensureCapacity(sz);
+                       for (int i = cList.size(); i <= sz; i++)
+                               cList.add(new DimConstraint());
+               }
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       @Override
+       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+
+       @Override
+       public void writeExternal(ObjectOutput out) throws IOException
+       {
+               if (getClass() == AC.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+}
\ No newline at end of file
diff --git a/srcjar/net/miginfocom/layout/AnimSpec.java b/srcjar/net/miginfocom/layout/AnimSpec.java
new file mode 100644 (file)
index 0000000..c1ece4f
--- /dev/null
@@ -0,0 +1,107 @@
+package net.miginfocom.layout;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ */
+
+import java.io.Serializable;
+
+/**
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 14-09-24
+ *         Time: 17:05
+ */
+public class AnimSpec implements Serializable
+{
+//     public static final AnimSpec OFF = new AnimSpec(-1, 0, 0);
+       public static final AnimSpec DEF = new AnimSpec(0, 0, 0.2f, 0.2f);
+
+       private final int prio;
+       private final int durMillis;
+       private final float easeIn, easeOut;
+
+       /**
+        * @param prio The animation priority. When added with the general animation priority of the layout the animation will
+        * be done if the resulting value is &gt; 0.
+        * @param durMillis Duration in milliseconds. &lt;=0 means default value should be used and &gt; 0 is the number of millis
+        * @param easeIn 0 is linear (no ease). 1 is max ease. Always clamped between these values.
+        * @param easeOut 0 is linear (no ease). 1 is max ease. Always clamped between these values.
+        */
+       public AnimSpec(int prio, int durMillis, float easeIn, float easeOut)
+       {
+               this.prio = prio;
+               this.durMillis = durMillis;
+               this.easeIn = LayoutUtil.clamp(easeIn, 0, 1);
+               this.easeOut = LayoutUtil.clamp(easeOut, 0, 1);
+       }
+
+       /**
+        * @return The animation priority. When added with the general animation priority of the layout the animation will
+        * be done if the resulting value is &gt; 0.
+        */
+       public int getPriority()
+       {
+               return prio;
+       }
+
+       /**
+        * @param defMillis Default used if the millis in the spec is set to "default".
+        * @return Duration in milliseconds. &lt;=0 means default value should be used and &gt; 0 is the number of millis
+        */
+       public int getDurationMillis(int defMillis)
+       {
+               return durMillis > 0 ? durMillis : defMillis;
+       }
+
+       /**
+        * @return Duration in milliseconds. &lt;= 0 means default value should be used and &gt; 0 is the number of millis
+        */
+       public int getDurationMillis()
+       {
+               return durMillis;
+       }
+
+       /**
+        * @return A value between 0 and 1 where 0 is no ease in and 1 is maximum ease in.
+        */
+       public float getEaseIn()
+       {
+               return easeIn;
+       }
+
+       /**
+        * @return A value between 0 and 1 where 0 is no ease out and 1 is maximum ease out.
+        */
+       public float getEaseOut()
+       {
+               return easeOut;
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/BoundSize.java b/srcjar/net/miginfocom/layout/BoundSize.java
new file mode 100644 (file)
index 0000000..531235f
--- /dev/null
@@ -0,0 +1,289 @@
+package net.miginfocom.layout;
+
+import java.beans.Encoder;
+import java.beans.Expression;
+import java.beans.PersistenceDelegate;
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A size that contains minimum, preferred and maximum size of type {@link UnitValue}.
+ * <p>
+ * This class is a simple value container and it is immutable.
+ * <p>
+ * If a size is missing (i.e., <code>null</code>) that boundary should be considered "not in use".
+ * <p>
+ * You can create a BoundSize from a String with the use of {@link ConstraintParser#parseBoundSize(String, boolean, boolean)}
+ */
+public class BoundSize implements Serializable
+{
+       public static final BoundSize NULL_SIZE = new BoundSize(null, null);
+       public static final BoundSize ZERO_PIXEL = new BoundSize(UnitValue.ZERO, "0px");
+
+       private final transient UnitValue min;
+       private final transient UnitValue pref;
+       private final transient UnitValue max;
+       private final transient boolean gapPush;
+
+       /** Constructor that use the same value for min/preferred/max size.
+        * @param minMaxPref The value to use for min/preferred/max size.
+        * @param createString The string used to create the BoundsSize.
+        */
+       public BoundSize(UnitValue minMaxPref, String createString)
+       {
+               this(minMaxPref, minMaxPref, minMaxPref, createString);
+       }
+
+       /** Constructor. <b>This method is here for serialization only and should normally not be used.</b> Use
+        * {@link ConstraintParser#parseBoundSize(String, boolean, boolean)} instead.
+        * @param min The minimum size. May be <code>null</code>.
+        * @param preferred  The preferred size. May be <code>null</code>.
+        * @param max  The maximum size. May be <code>null</code>.
+        * @param createString The string used to create the BoundsSize.
+        */
+       public BoundSize(UnitValue min, UnitValue preferred, UnitValue max, String createString)    // Bound to old delegate!!!!!
+       {
+               this(min, preferred, max, false, createString);
+       }
+
+       /** Constructor. <b>This method is here for serialization only and should normally not be used.</b> Use
+        * {@link ConstraintParser#parseBoundSize(String, boolean, boolean)} instead.
+        * @param min The minimum size. May be <code>null</code>.
+        * @param preferred  The preferred size. May be <code>null</code>.
+        * @param max  The maximum size. May be <code>null</code>.
+        * @param gapPush If the size should be hinted as "pushing" and thus want to occupy free space if no one else is claiming it.
+        * @param createString The string used to create the BoundsSize.
+        */
+       public BoundSize(UnitValue min, UnitValue preferred, UnitValue max, boolean gapPush, String createString)
+       {
+               this.min = min;
+               this.pref = preferred;
+               this.max = max;
+               this.gapPush = gapPush;
+
+               LayoutUtil.putCCString(this, createString);    // this escapes!!
+       }
+
+       /** Returns the minimum size as sent into the constructor.
+        * @return The minimum size as sent into the constructor. May be <code>null</code>.
+        */
+       public final UnitValue getMin()
+       {
+               return min;
+       }
+
+       /** Returns the preferred size as sent into the constructor.
+        * @return The preferred size as sent into the constructor. May be <code>null</code>.
+        */
+       public final UnitValue getPreferred()
+       {
+               return pref;
+       }
+
+       /** Returns the maximum size as sent into the constructor.
+        * @return The maximum size as sent into the constructor. May be <code>null</code>.
+        */
+       public final UnitValue getMax()
+       {
+               return max;
+       }
+
+       /** If the size should be hinted as "pushing" and thus want to occupy free space if no one else is claiming it.
+        * @return The value.
+        */
+       public boolean getGapPush()
+       {
+               return gapPush;
+       }
+
+       /** Returns if this bound size has no min, preferred and maximum size set (they are all <code>null</code>)
+        * @return If unset.
+        */
+       public boolean isUnset()
+       {
+               // Most common case by far is this == ZERO_PIXEL...
+               return this == ZERO_PIXEL || (pref == null && min == null && max == null && gapPush == false);
+       }
+
+       /** Makes sure that <code>size</code> is within min and max of this size.
+        * @param size The size to constrain.
+        * @param refValue The reference to use for relative sizes.
+        * @param parent The parent container.
+        * @return The size, constrained within min and max.
+        */
+       public int constrain(int size, float refValue, ContainerWrapper parent)
+       {
+               if (max != null)
+                       size = Math.min(size, max.getPixels(refValue, parent, parent));
+               if (min != null)
+                       size = Math.max(size, min.getPixels(refValue, parent, parent));
+               return size;
+       }
+
+       /** Returns the minimum, preferred or maximum size for this bounded size.
+        * @param sizeType The type. <code>LayoutUtil.MIN</code>, <code>LayoutUtil.PREF</code> or <code>LayoutUtil.MAX</code>.
+        * @return
+        */
+       final UnitValue getSize(int sizeType)
+       {
+               switch(sizeType) {
+                       case LayoutUtil.MIN:
+                               return min;
+                       case LayoutUtil.PREF:
+                               return pref;
+                       case LayoutUtil.MAX:
+                               return max;
+                       default:
+                               throw new IllegalArgumentException("Unknown size: " + sizeType);
+               }
+       }
+
+       /** Convert the bound sizes to pixels.
+        * <p>
+        * <code>null</code> bound sizes will be 0 for min and preferred and {@link net.miginfocom.layout.LayoutUtil#INF} for max.
+        * @param refSize The reference size.
+        * @param parent The parent. Not <code>null</code>.
+        * @param comp The component, if applicable, can be <code>null</code>.
+        * @return An array of length three (min,pref,max).
+        */
+       final int[] getPixelSizes(float refSize, ContainerWrapper parent, ComponentWrapper comp)
+       {
+               return new int[] {
+                               min != null ? min.getPixels(refSize, parent, comp) : 0,
+                               pref != null ? pref.getPixels(refSize, parent, comp) : 0,
+                               max != null ? max.getPixels(refSize, parent, comp) : LayoutUtil.INF
+               };
+       }
+
+       /** Returns the a constraint string that can be re-parsed to be the exact same UnitValue.
+        * @return A String. Never <code>null</code>.
+        */
+       String getConstraintString()
+       {
+               String cs = LayoutUtil.getCCString(this);
+               if (cs != null)
+                       return cs;
+
+               if (min == pref && pref == max)
+                       return min != null ? (min.getConstraintString() + "!") : "null";
+
+               StringBuilder sb = new StringBuilder(16);
+
+               if (min != null)
+                       sb.append(min.getConstraintString()).append(':');
+
+               if (pref != null) {
+                       if (min == null && max != null)
+                               sb.append(":");
+                       sb.append(pref.getConstraintString());
+               } else if (min != null) {
+                       sb.append('n');
+               }
+
+               if (max != null)
+                       sb.append(sb.length() == 0 ? "::" : ":").append(max.getConstraintString());
+
+               if (gapPush) {
+                       if (sb.length() > 0)
+                               sb.append(':');
+                       sb.append("push");
+               }
+
+               return sb.toString();
+       }
+
+       void checkNotLinked()
+       {
+               if (isLinked())
+                       throw new IllegalArgumentException("Size may not contain links");
+       }
+
+       boolean isLinked()
+       {
+               return min != null && min.isLinkedDeep() || pref != null && pref.isLinkedDeep() || max != null && max.isLinkedDeep();
+       }
+
+       boolean isAbsolute()
+       {
+               return (min == null || min.isAbsoluteDeep()) && (pref == null || pref.isAbsoluteDeep()) && (max == null || max.isAbsoluteDeep());
+       }
+
+       public String toString()
+       {
+               return "BoundSize{" + "min=" + min + ", pref=" + pref + ", max=" + max + ", gapPush=" + gapPush +'}';
+       }
+
+       static {
+        if(LayoutUtil.HAS_BEANS){
+            LayoutUtil.setDelegate(BoundSize.class, new PersistenceDelegate() {
+                @Override
+                protected Expression instantiate(Object oldInstance, Encoder out)
+                {
+                    BoundSize bs = (BoundSize) oldInstance;
+                    if (Grid.TEST_GAPS) {
+                        return new Expression(oldInstance, BoundSize.class, "new", new Object[] {
+                                bs.getMin(), bs.getPreferred(), bs.getMax(), bs.getGapPush(), bs.getConstraintString()
+                        });
+                    } else {
+                        return new Expression(oldInstance, BoundSize.class, "new", new Object[] {
+                                bs.getMin(), bs.getPreferred(), bs.getMax(), bs.getConstraintString()
+                        });
+                    }
+                }
+            });
+        }
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private static final long serialVersionUID = 1L;
+
+       protected Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       private void writeObject(ObjectOutputStream out) throws IOException
+       {
+               if (getClass() == BoundSize.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+
+       private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/CC.java b/srcjar/net/miginfocom/layout/CC.java
new file mode 100644 (file)
index 0000000..7206733
--- /dev/null
@@ -0,0 +1,1881 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+import java.util.ArrayList;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A simple value holder for one component's constraint.
+ */
+public final class CC implements Externalizable
+{
+       private static final BoundSize DEF_GAP = BoundSize.NULL_SIZE;    // Only used to denote default wrap/newline gap.
+
+       static final String[] DOCK_SIDES = {"north", "west", "south", "east"};
+
+       // See the getters and setters for information about the properties below.
+
+       private int dock = -1;
+
+       private UnitValue[] pos = null; // [x1, y1, x2, y2]
+
+       private UnitValue[] padding = null;   // top, left, bottom, right
+
+       private UnitValue[] visualPadding = null;   // top, left, bottom, right
+
+       private Boolean flowX = null;
+
+       private int skip = 0;
+
+       private int split = 1;
+
+       private int spanX = 1, spanY = 1;
+
+       private int cellX = -1, cellY = 0; // If cellX is -1 then cellY is also considered -1. cellY is never negative.
+
+       private String tag = null;
+
+       private String id = null;
+
+       private int hideMode = -1;
+
+       private DimConstraint hor = new DimConstraint();
+
+       private DimConstraint ver = new DimConstraint();
+
+       private BoundSize newline = null;
+
+       private BoundSize wrap = null;
+
+       private boolean boundsInGrid = true;
+
+       private boolean external = false;
+
+       private Float pushX = null, pushY = null;
+
+       private AnimSpec animSpec = AnimSpec.DEF;
+
+
+       // ***** Tmp cache field
+
+       private static final String[] EMPTY_ARR = new String[0];
+
+       private transient String[] linkTargets = null;
+
+       /** Empty constructor.
+        */
+       public CC()
+       {
+       }
+
+       String[] getLinkTargets()
+       {
+               if (linkTargets == null) {
+                       final ArrayList<String> targets = new ArrayList<String>(2);
+
+                       if (pos != null) {
+                               for (int i = 0; i < pos.length ; i++)
+                                       addLinkTargetIDs(targets, pos[i]);
+                       }
+
+                       linkTargets = targets.size() == 0 ? EMPTY_ARR : targets.toArray(new String[targets.size()]);
+               }
+               return linkTargets;
+       }
+
+       private void addLinkTargetIDs(ArrayList<String> targets, UnitValue uv)
+       {
+               if (uv != null) {
+                       String linkId = uv.getLinkTargetId();
+                       if (linkId != null) {
+                               targets.add(linkId);
+                       } else {
+                               for (int i = uv.getSubUnitCount() - 1; i >= 0; i--) {
+                                       UnitValue subUv = uv.getSubUnitValue(i);
+                                       if (subUv.isLinkedDeep())
+                                               addLinkTargetIDs(targets, subUv);
+                               }
+                       }
+               }
+       }
+
+       // **********************************************************
+       // Chaining constraint setters
+       // **********************************************************
+
+       /** Specifies that the component should be put in the end group <code>s</code> and will thus share the same ending
+        * coordinate as them within the group.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC endGroupX(String s)
+       {
+               hor.setEndGroup(s);
+               return this;
+       }
+
+       /** Specifies that the component should be put in the size group <code>s</code> and will thus share the same size
+        * as them within the group.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC sizeGroupX(String s)
+       {
+               hor.setSizeGroup(s);
+               return this;
+       }
+
+       /** The minimum size for the component. The value will override any value that is set on the component itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC minWidth(String size)
+       {
+               hor.setSize(LayoutUtil.derive(hor.getSize(), ConstraintParser.parseUnitValue(size, true), null, null));
+               return this;
+       }
+
+       /** The size for the component as a min and/or preferred and/or maximum size. The value will override any value that is set on
+        * the component itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The size expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC width(String size)
+       {
+               hor.setSize(ConstraintParser.parseBoundSize(size, false, true));
+               return this;
+       }
+
+       /** The maximum size for the component. The value will override any value that is set on the component itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC maxWidth(String size)
+       {
+               hor.setSize(LayoutUtil.derive(hor.getSize(), null, null, ConstraintParser.parseUnitValue(size, true)));
+               return this;
+       }
+
+
+       /** The horizontal gap before and/or after the component. The gap is towards cell bounds and/or other component bounds.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param before The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @param after The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC gapX(String before, String after)
+       {
+               if (before != null)
+                       hor.setGapBefore(ConstraintParser.parseBoundSize(before, true, true));
+
+               if (after != null)
+                       hor.setGapAfter(ConstraintParser.parseBoundSize(after, true, true));
+
+               return this;
+       }
+
+       /** Same functionality as <code>getHorizontal().setAlign(ConstraintParser.parseUnitValue(unitValue, true))</code> only this method
+        * returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param align The align keyword or for instance "100px". E.g "left", "right", "leading" or "trailing".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC alignX(String align)
+       {
+               hor.setAlign(ConstraintParser.parseUnitValueOrAlign(align, true, null));
+               return this;
+       }
+
+       /** The grow priority compared to other components in the same cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The grow priority.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC growPrioX(int p)
+       {
+               hor.setGrowPriority(p);
+               return this;
+       }
+
+       /** Grow priority for the component horizontally and optionally vertically.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC growPrio(int ... widthHeight)
+       {
+               switch (widthHeight.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+                       case 2:
+                               growPrioY(widthHeight[1]);
+                       case 1:
+                               growPrioX(widthHeight[0]);
+               }
+               return this;
+       }
+
+       /** Grow weight for the component horizontally. It default to weight <code>100</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #growX(float)
+        */
+       public final CC growX()
+       {
+               hor.setGrow(ResizeConstraint.WEIGHT_100);
+               return this;
+       }
+
+       /** Grow weight for the component horizontally.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new grow weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC growX(float w)
+       {
+               hor.setGrow(new Float(w));
+               return this;
+       }
+
+       /** grow weight for the component horizontally and optionally vertically.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC grow(float ... widthHeight)
+       {
+               switch (widthHeight.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+                       case 2:
+                               growY(widthHeight[1]);
+                       case 1:
+                               growX(widthHeight[0]);
+               }
+               return this;
+       }
+
+       /** The shrink priority compared to other components in the same cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The shrink priority.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC shrinkPrioX(int p)
+       {
+               hor.setShrinkPriority(p);
+               return this;
+       }
+
+       /** Shrink priority for the component horizontally and optionally vertically.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC shrinkPrio(int ... widthHeight)
+       {
+               switch (widthHeight.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+                       case 2:
+                               shrinkPrioY(widthHeight[1]);
+                       case 1:
+                               shrinkPrioX(widthHeight[0]);
+               }
+               return this;
+       }
+
+       /** Shrink weight for the component horizontally.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new shrink weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC shrinkX(float w)
+       {
+               hor.setShrink(new Float(w));
+               return this;
+       }
+
+       /** Shrink weight for the component horizontally and optionally vertically.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC shrink(float ... widthHeight)
+       {
+               switch (widthHeight.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+                       case 2:
+                               shrinkY(widthHeight[1]);
+                       case 1:
+                               shrinkX(widthHeight[0]);
+               }
+               return this;
+       }
+
+       /** The end group that this component should be placed in.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The name of the group. If <code>null</code> that means no group (default)
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC endGroupY(String s)
+       {
+               ver.setEndGroup(s);
+               return this;
+       }
+
+       /** The end group(s) that this component should be placed in.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param xy The end group for x and y respectively. 1-2 arguments, not null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC endGroup(String ... xy)
+       {
+               switch (xy.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + xy.length);
+                       case 2:
+                               endGroupY(xy[1]);
+                       case 1:
+                               endGroupX(xy[0]);
+               }
+               return this;
+       }
+
+       /** The size group that this component should be placed in.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The name of the group. If <code>null</code> that means no group (default)
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC sizeGroupY(String s)
+       {
+               ver.setSizeGroup(s);
+               return this;
+       }
+
+       /** The size group(s) that this component should be placed in.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param xy The size group for x and y respectively. 1-2 arguments, not null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC sizeGroup(String ... xy)
+       {
+               switch (xy.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + xy.length);
+                       case 2:
+                               sizeGroupY(xy[1]);
+                       case 1:
+                               sizeGroupX(xy[0]);
+               }
+               return this;
+       }
+
+       /** The minimum size for the component. The value will override any value that is set on the component itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC minHeight(String size)
+       {
+               ver.setSize(LayoutUtil.derive(ver.getSize(), ConstraintParser.parseUnitValue(size, false), null, null));
+               return this;
+       }
+
+       /** The size for the component as a min and/or preferred and/or maximum size. The value will override any value that is set on
+        * the component itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The size expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC height(String size)
+       {
+               ver.setSize(ConstraintParser.parseBoundSize(size, false, false));
+               return this;
+       }
+
+       /** The maximum size for the component. The value will override any value that is set on the component itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC maxHeight(String size)
+       {
+               ver.setSize(LayoutUtil.derive(ver.getSize(), null, null, ConstraintParser.parseUnitValue(size, false)));
+               return this;
+       }
+
+       /** The vertical gap before (normally above) and/or after (normally below) the component. The gap is towards cell bounds and/or other component bounds.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param before The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @param after The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC gapY(String before, String after)
+       {
+               if (before != null)
+                       ver.setGapBefore(ConstraintParser.parseBoundSize(before, true, false));
+
+               if (after != null)
+                       ver.setGapAfter(ConstraintParser.parseBoundSize(after, true, false));
+
+               return this;
+       }
+
+       /** Same functionality as <code>getVertical().setAlign(ConstraintParser.parseUnitValue(unitValue, true))</code> only this method
+        * returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param align The align keyword or for instance "100px". E.g "top" or "bottom".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC alignY(String align)
+       {
+               ver.setAlign(ConstraintParser.parseUnitValueOrAlign(align, false, null));
+               return this;
+       }
+
+       /** The grow priority compared to other components in the same cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The grow priority.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC growPrioY(int p)
+       {
+               ver.setGrowPriority(p);
+               return this;
+       }
+
+       /** Grow weight for the component vertically. Defaults to <code>100</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #growY(Float)
+        */
+       public final CC growY()
+       {
+               ver.setGrow(ResizeConstraint.WEIGHT_100);
+               return this;
+       }
+
+       /** Grow weight for the component vertically.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new grow weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC growY(float w)
+       {
+               ver.setGrow(w);
+               return this;
+       }
+
+       /** Grow weight for the component vertically.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new grow weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       @Deprecated
+       public final CC growY(Float w)
+       {
+               ver.setGrow(w);
+               return this;
+       }
+
+       /** The shrink priority compared to other components in the same cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The shrink priority.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC shrinkPrioY(int p)
+       {
+               ver.setShrinkPriority(p);
+               return this;
+       }
+
+       /** Shrink weight for the component horizontally.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param w The new shrink weight.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC shrinkY(float w)
+       {
+               ver.setShrink(new Float(w));
+               return this;
+       }
+
+       /** How this component, if hidden (not visible), should be treated.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param mode The mode. Default to the mode in the {@link net.miginfocom.layout.LC}.
+        * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+        * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+        * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+        * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC hideMode(int mode)
+       {
+               setHideMode(mode);
+               return this;
+       }
+
+       /** The id used to reference this component in some constraints.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The id or <code>null</code>. May consist of a groupID and an componentID which are separated by a dot: ".". E.g. "grp1.id1".
+        * The dot should never be first or last if present.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        */
+       public final CC id(String s)
+       {
+               setId(s);
+               return this;
+       }
+
+       /** Same functionality as {@link #setTag(String tag)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param tag The new tag. May be <code>null</code>.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setTag(String)
+        */
+       public final CC tag(String tag)
+       {
+               setTag(tag);
+               return this;
+       }
+
+       /** Set the cell(s) that the component should occupy in the grid. Same functionality as {@link #setCellX(int col)} and
+        * {@link #setCellY(int row)} together with {@link #setSpanX(int width)} and {@link #setSpanY(int height)}. This method
+        * returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param colRowWidthHeight cellX, cellY, spanX, spanY respectively. 1-4 arguments, not null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setCellX(int)
+        * @see #setCellY(int)
+        * @see #setSpanX(int)
+        * @see #setSpanY(int)
+        * @since 3.7.2. Replacing cell(int, int) and cell(int, int, int, int)
+        */
+       public final CC cell(int ... colRowWidthHeight)
+       {
+               switch (colRowWidthHeight.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + colRowWidthHeight.length);
+                       case 4:
+                               setSpanY(colRowWidthHeight[3]);
+                       case 3:
+                               setSpanX(colRowWidthHeight[2]);
+                       case 2:
+                               setCellY(colRowWidthHeight[1]);
+                       case 1:
+                               setCellX(colRowWidthHeight[0]);
+               }
+               return this;
+       }
+
+       /** Same functionality as <code>spanX(cellsX).spanY(cellsY)</code> which means this cell will span cells in both x and y.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * Since 3.7.2 this takes an array/vararg whereas it previously only took two specific values, xSpan and ySpan.
+        * @param cells spanX and spanY, when present, and in that order.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setSpanY(int)
+        * @see #setSpanX(int)
+        * @see #spanY()
+        * @see #spanX()
+        * @since 3.7.2 Replaces span(int, int).
+        */
+       public final CC span(int ... cells)
+       {
+               if (cells == null || cells.length == 0) {
+                       setSpanX(LayoutUtil.INF);
+                       setSpanY(1);
+               } else if (cells.length == 1) {
+                       setSpanX(cells[0]);
+                       setSpanY(1);
+               } else {
+                       setSpanX(cells[0]);
+                       setSpanY(cells[1]);
+               }
+               return this;
+       }
+
+       /** Corresponds exactly to the "gap left right top bottom" keyword.
+        * @param args Same as for the "gap" keyword. Length 1-4, never null buf elements can be null.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gap(String ... args)
+       {
+               switch (args.length) {
+                       default:
+                               throw new IllegalArgumentException("Illegal argument count: " + args.length);
+                       case 4:
+                               gapBottom(args[3]);
+                       case 3:
+                               gapTop(args[2]);
+                       case 2:
+                               gapRight(args[1]);
+                       case 1:
+                               gapLeft(args[0]);
+               }
+               return this;
+       }
+
+       /** Sets the horizontal gap before the component.
+        * <p>
+        * Note! This is currently same as gapLeft(). This might change in 4.x.
+        * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gapBefore(String boundsSize)
+       {
+               hor.setGapBefore(ConstraintParser.parseBoundSize(boundsSize, true, true));
+               return this;
+       }
+
+       /** Sets the horizontal gap after the component.
+        * <p>
+        * Note! This is currently same as gapRight(). This might change in 4.x.
+        * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gapAfter(String boundsSize)
+       {
+               hor.setGapAfter(ConstraintParser.parseBoundSize(boundsSize, true, true));
+               return this;
+       }
+
+       /** Sets the gap above the component.
+        * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gapTop(String boundsSize)
+       {
+               ver.setGapBefore(ConstraintParser.parseBoundSize(boundsSize, true, false));
+               return this;
+       }
+
+       /** Sets the gap to the left the component.
+        * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gapLeft(String boundsSize)
+       {
+               hor.setGapBefore(ConstraintParser.parseBoundSize(boundsSize, true, true));
+               return this;
+       }
+
+       /** Sets the gap below the component.
+        * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gapBottom(String boundsSize)
+       {
+               ver.setGapAfter(ConstraintParser.parseBoundSize(boundsSize, true, false));
+               return this;
+       }
+
+       /** Sets the gap to the right of the component.
+        * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final CC gapRight(String boundsSize)
+       {
+               hor.setGapAfter(ConstraintParser.parseBoundSize(boundsSize, true, true));
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setSpanY(int)} with <code>LayoutUtil.INF</code> which means this cell will span the rest of the column.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setSpanY(int)
+        * @see #spanY()
+        */
+       public final CC spanY()
+       {
+               return spanY(LayoutUtil.INF);
+       }
+
+       /** Same functionality as {@link #setSpanY(int)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param cells The number of cells to span (i.e. merge).
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setSpanY(int)
+        */
+       public final CC spanY(int cells)
+       {
+               setSpanY(cells);
+               return this;
+       }
+
+       /** Same functionality as {@link #setSpanX(int)} which means this cell will span the rest of the row.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setSpanX(int)
+        * @see #spanX()
+        */
+       public final CC spanX()
+       {
+               return spanX(LayoutUtil.INF);
+       }
+
+       /** Same functionality as {@link #setSpanX(int)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param cells The number of cells to span (i.e. merge).
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setSpanY(int)
+        */
+       public final CC spanX(int cells)
+       {
+               setSpanX(cells);
+               return this;
+       }
+
+       /** Same functionality as <code>pushX().pushY()</code> which means this cell will push in both x and y dimensions.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setPushX(Float)
+        * @see #setPushX(Float)
+        * @see #pushY()
+        * @see #pushX()
+        */
+       public final CC push()
+       {
+               return pushX().pushY();
+       }
+
+       /** Same functionality as <code>pushX(weightX).pushY(weightY)</code> which means this cell will push in both x and y dimensions.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param weightX The weight used in the push.
+        * @param weightY The weight used in the push.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setPushY(Float)
+        * @see #setPushX(Float)
+        * @see #pushY()
+        * @see #pushX()
+        */
+       public final CC push(Float weightX, Float weightY)
+       {
+               return pushX(weightX).pushY(weightY);
+       }
+
+       /** Same functionality as {@link #setPushY(Float)} which means this cell will push the rest of the column.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setPushY(Float)
+        */
+       public final CC pushY()
+       {
+               return pushY(ResizeConstraint.WEIGHT_100);
+       }
+
+       /** Same functionality as {@link #setPushY(Float weight)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param weight The weight used in the push.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setPushY(Float)
+        */
+       public final CC pushY(Float weight)
+       {
+               setPushY(weight);
+               return this;
+       }
+
+       /** Same functionality as {@link #setPushX(Float)} which means this cell will push the rest of the row.
+        * This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setPushX(Float)
+        */
+       public final CC pushX()
+       {
+               return pushX(ResizeConstraint.WEIGHT_100);
+       }
+
+       /** Same functionality as {@link #setPushX(Float weight)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param weight The weight used in the push.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setPushY(Float)
+        */
+       public final CC pushX(Float weight)
+       {
+               setPushX(weight);
+               return this;
+       }
+
+       /** Same functionality as {@link #setSplit(int parts)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param parts The number of parts (i.e. component slots) the cell should be divided into.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setSplit(int)
+        */
+       public final CC split(int parts)
+       {
+               setSplit(parts);
+               return this;
+       }
+
+       /** Same functionality as split(LayoutUtil.INF), which means split until one of the keywords that breaks the split is found for
+        * a component after this one (e.g. wrap, newline and skip).
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setSplit(int)
+        * @since 3.7.2
+        */
+       public final CC split()
+       {
+               setSplit(LayoutUtil.INF);
+               return this;
+       }
+
+       /** Same functionality as {@link #setSkip(int)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param cells How many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setSkip(int)
+        */
+       public final CC skip(int cells)
+       {
+               setSkip(cells);
+               return this;
+       }
+
+       /** Same functionality as skip(1).
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setSkip(int)
+        * @since 3.7.2
+        */
+       public final CC skip()
+       {
+               setSkip(1);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setExternal(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setExternal(boolean)
+        */
+       public final CC external()
+       {
+               setExternal(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFlowX(Boolean)} with <code>Boolean.TRUE</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setFlowX(Boolean)
+        */
+       public final CC flowX()
+       {
+               setFlowX(Boolean.TRUE);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFlowX(Boolean)} with <code>Boolean.FALSE</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setFlowX(Boolean)
+        */
+       public final CC flowY()
+       {
+               setFlowX(Boolean.FALSE);
+               return this;
+       }
+
+
+       /** Same functionality as {@link #growX()} and {@link #growY()}.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #growX()
+        * @see #growY()
+        */
+       public final CC grow()
+       {
+               growX();
+               growY();
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setNewline(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setNewline(boolean)
+        */
+       public final CC newline()
+       {
+               setNewline(true);
+               return this;
+       }
+
+       /** Same functionality as {@link #setNewlineGapSize(BoundSize)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param gapSize The gap size that will override the gap size in the row/column constraints if <code>!= null</code>. E.g. "5px" or "unrel".
+        * If <code>null</code> or <code>""</code> the newline size will be set to the default size and turned on. This is different compared to
+        * {@link #setNewlineGapSize(BoundSize)}.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setNewlineGapSize(BoundSize)
+        */
+       public final CC newline(String gapSize)
+       {
+               BoundSize bs = ConstraintParser.parseBoundSize(gapSize, true, (flowX != null && flowX == false));
+               if (bs != null) {
+                       setNewlineGapSize(bs);
+               } else {
+                       setNewline(true);
+               }
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setWrap(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setWrap(boolean)
+        */
+       public final CC wrap()
+       {
+               setWrap(true);
+               return this;
+       }
+
+       /** Same functionality as {@link #setWrapGapSize(BoundSize)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param gapSize The gap size that will override the gap size in the row/column constraints if <code>!= null</code>. E.g. "5px" or "unrel".
+        * If <code>null</code> or <code>""</code> the wrap size will be set to the default size and turned on. This is different compared to
+        * {@link #setWrapGapSize(BoundSize)}.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setWrapGapSize(BoundSize)
+        */
+       public final CC wrap(String gapSize)
+       {
+               BoundSize bs = ConstraintParser.parseBoundSize(gapSize, true, (flowX != null && flowX == false));
+               if (bs != null) {
+                       setWrapGapSize(bs);
+               } else {
+                       setWrap(true);
+               }
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setDockSide(int)} with <code>0</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setDockSide(int)
+        */
+       public final CC dockNorth()
+       {
+               setDockSide(0);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setDockSide(int)} with <code>1</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setDockSide(int)
+        */
+       public final CC dockWest()
+       {
+               setDockSide(1);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setDockSide(int)} with <code>2</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setDockSide(int)
+        */
+       public final CC dockSouth()
+       {
+               setDockSide(2);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setDockSide(int)} with <code>3</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setDockSide(int)
+        */
+       public final CC dockEast()
+       {
+               setDockSide(3);
+               return this;
+       }
+
+       /** Sets the x-coordinate for the component. This is used to set the x coordinate position to a specific value. The component
+        * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the x position.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param x The x position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setPos(UnitValue[])
+        * @see #setBoundsInGrid(boolean)
+        */
+       public final CC x(String x)
+       {
+               return corrPos(x, 0);
+       }
+
+       /** Sets the y-coordinate for the component. This is used to set the y coordinate position to a specific value. The component
+        * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the y position.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param y The y position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setPos(UnitValue[])
+        * @see #setBoundsInGrid(boolean)
+        */
+       public final CC y(String y)
+       {
+               return corrPos(y, 1);
+       }
+
+       /** Sets the x2-coordinate for the component (right side). This is used to set the x2 coordinate position to a specific value. The component
+        * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the x position.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param x2 The x2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setPos(UnitValue[])
+        * @see #setBoundsInGrid(boolean)
+        */
+       public final CC x2(String x2)
+       {
+               return corrPos(x2, 2);
+       }
+
+       /** Sets the y2-coordinate for the component (bottom side). This is used to set the y2 coordinate position to a specific value. The component
+        * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the y position.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param y2 The y2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setPos(UnitValue[])
+        * @see #setBoundsInGrid(boolean)
+        */
+       public final CC y2(String y2)
+       {
+               return corrPos(y2, 3);
+       }
+
+       private final CC corrPos(String uv, int ix)
+       {
+               UnitValue[] b = getPos();
+               if (b == null)
+                       b = new UnitValue[4];
+
+               b[ix] = ConstraintParser.parseUnitValue(uv, (ix % 2 == 0));
+               setPos(b);
+
+               setBoundsInGrid(true);
+               return this;
+       }
+
+       /** Same functionality as {@link #x(String x)} and {@link #y(String y)} together.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param x The x position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+        * @param y The y position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setPos(UnitValue[])
+        */
+       public final CC pos(String x, String y)
+       {
+               UnitValue[] b = getPos();
+               if (b == null)
+                       b = new UnitValue[4];
+
+               b[0] = ConstraintParser.parseUnitValue(x, true);
+               b[1] = ConstraintParser.parseUnitValue(y, false);
+               setPos(b);
+
+               setBoundsInGrid(false);
+               return this;
+       }
+
+       /** Same functionality as {@link #x(String x)}, {@link #y(String y)}, {@link #y2(String y)} and {@link #y2(String y)} together.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param x The x position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+        * @param y The y position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+        * @param x2 The x2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+        * @param y2 The y2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setPos(UnitValue[])
+        */
+       public final CC pos(String x, String y, String x2, String y2)
+       {
+               setPos(new UnitValue[] {
+                               ConstraintParser.parseUnitValue(x, true),
+                               ConstraintParser.parseUnitValue(y, false),
+                               ConstraintParser.parseUnitValue(x2, true),
+                               ConstraintParser.parseUnitValue(y2, false),
+               });
+               setBoundsInGrid(false);
+               return this;
+       }
+
+       /** Same functionality as {@link #setPadding(UnitValue[])} but the unit values as absolute pixels. This method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param top The top padding that will be added to the y coordinate at the last stage in the layout.
+        * @param left The top padding that will be added to the x coordinate at the last stage in the layout.
+        * @param bottom The top padding that will be added to the y2 coordinate at the last stage in the layout.
+        * @param right The top padding that will be added to the x2 coordinate at the last stage in the layout.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setTag(String)
+        */
+       public final CC pad(int top, int left, int bottom, int right)
+       {
+               setPadding(new UnitValue[] {
+                               new UnitValue(top),     new UnitValue(left), new UnitValue(bottom), new UnitValue(right)
+               });
+               return this;
+       }
+
+       /** Same functionality as <code>setPadding(ConstraintParser.parseInsets(pad, false))}</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param pad The string to parse. E.g. "10 10 10 10" or "20". If less than 4 groups the last will be used for the missing.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+        * @see #setTag(String)
+        */
+       public final CC pad(String pad)
+       {
+               setPadding(pad != null ? ConstraintParser.parseInsets(pad, false) : null);
+               return this;
+       }
+
+       // **********************************************************
+       // Bean properties
+       // **********************************************************
+
+       /** Returns the horizontal dimension constraint for this component constraint. It has constraints for the horizontal size
+        * and grow/shrink priorities and weights.
+        * <p>
+        * Note! If any changes is to be made it must be made direct when the object is returned. It is not allowed to save the
+        * constraint for later use.
+        * @return The current dimension constraint. Never <code>null</code>.
+        */
+       public DimConstraint getHorizontal()
+       {
+               return hor;
+       }
+
+       /** Sets the horizontal dimension constraint for this component constraint. It has constraints for the horizontal size
+        * and grow/shrink priorities and weights.
+        * @param h The new dimension constraint. If <code>null</code> it will be reset to <code>new DimConstraint();</code>
+        */
+       public void setHorizontal(DimConstraint h)
+       {
+               hor = h != null ? h : new DimConstraint();
+       }
+
+       /** Returns the vertical dimension constraint for this component constraint. It has constraints for the vertical size
+        * and grow/shrink priorities and weights.
+        * <p>
+        * Note! If any changes is to be made it must be made direct when the object is returned. It is not allowed to save the
+        * constraint for later use.
+        * @return The current dimension constraint. Never <code>null</code>.
+        */
+       public DimConstraint getVertical()
+       {
+               return ver;
+       }
+
+       /** Sets the vertical dimension constraint for this component constraint. It has constraints for the vertical size
+        * and grow/shrink priorities and weights.
+        * @param v The new dimension constraint. If <code>null</code> it will be reset to <code>new DimConstraint();</code>
+        */
+       public void setVertical(DimConstraint v)
+       {
+               ver = v != null ? v : new DimConstraint();
+       }
+
+       /** Returns the vertical or horizontal dim constraint.
+        * <p>
+        * Note! If any changes is to be made it must be made direct when the object is returned. It is not allowed to save the
+        * constraint for later use.
+        * @param isHor If the horizontal constraint should be returned.
+        * @return The dim constraint. Never <code>null</code>.
+        */
+       public DimConstraint getDimConstraint(boolean isHor)
+       {
+               return isHor ? hor : ver;
+       }
+
+       /** Returns the absolute positioning of one or more of the edges. This will be applied last in the layout cycle and will not
+        * affect the flow or grid positions. The positioning is relative to the parent and can not (as padding) be used
+        * to adjust the edges relative to the old value. May be <code>null</code> and elements may be <code>null</code>.
+        * <code>null</code> value(s) for the x2 and y2 will be interpreted as to keep the preferred size and thus the x1
+        * and x2 will just absolutely positions the component.
+        * <p>
+        * Note that {@link #setBoundsInGrid(boolean)} changes the interpretation of this property slightly.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value as a new array, free to modify.
+        */
+       public UnitValue[] getPos()
+       {
+               return pos != null ? new UnitValue[] {pos[0], pos[1], pos[2], pos[3]} : null;
+       }
+
+       /** Sets absolute positioning of one or more of the edges. This will be applied last in the layout cycle and will not
+        * affect the flow or grid positions. The positioning is relative to the parent and can not (as padding) be used
+        * to adjust the edges relative to the old value. May be <code>null</code> and elements may be <code>null</code>.
+        * <code>null</code> value(s) for the x2 and y2 will be interpreted as to keep the preferred size and thus the x1
+        * and x2 will just absolutely positions the component.
+        * <p>
+        * Note that {@link #setBoundsInGrid(boolean)} changes the interpretation of this property slightly.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param pos <code>UnitValue[] {x, y, x2, y2}</code>. Must be <code>null</code> or of length 4. Elements can be <code>null</code>.
+        */
+       public void setPos(UnitValue[] pos)
+       {
+               this.pos = pos != null ? new UnitValue[] {pos[0], pos[1], pos[2], pos[3]} : null;
+               linkTargets = null;
+       }
+
+       /** Returns if the absolute <code>pos</code> value should be corrections to the component that is in a normal cell. If <code>false</code>
+        * the value of <code>pos</code> is truly absolute in that it will not affect the grid or have a default bounds in the grid.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        * @see #getPos()
+        */
+       public boolean isBoundsInGrid()
+       {
+               return boundsInGrid;
+       }
+
+       /** Sets if the absolute <code>pos</code> value should be corrections to the component that is in a normal cell. If <code>false</code>
+        * the value of <code>pos</code> is truly absolute in that it will not affect the grid or have a default bounds in the grid.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> for bounds taken from the grid position. <code>false</code> is default.
+        * @see #setPos(UnitValue[])
+        */
+       void setBoundsInGrid(boolean b)
+       {
+               this.boundsInGrid = b;
+       }
+
+       /** Returns the absolute cell position in the grid or <code>-1</code> if cell positioning is not used.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public int getCellX()
+       {
+               return cellX;
+       }
+
+       /** Set an absolute cell x-position in the grid. If &gt;= 0 this point points to the absolute cell that this constaint's component should occupy.
+        * If there's already a component in that cell they will split the cell. The flow will then continue after this cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param x The x-position or <code>-1</code> to disable cell positioning.
+        */
+       public void setCellX(int x)
+       {
+               cellX = x;
+       }
+
+       /** Returns the absolute cell position in the grid or <code>-1</code> if cell positioning is not used.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public int getCellY()
+       {
+               return cellX < 0 ? -1 : cellY;
+       }
+
+       /** Set an absolute cell x-position in the grid. If &gt;= 0 this point points to the absolute cell that this constaint's component should occupy.
+        * If there's already a component in that cell they will split the cell. The flow will then continue after this cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param y The y-position or <code>-1</code> to disable cell positioning.
+        */
+       public void setCellY(int y)
+       {
+               if (y < 0)
+                       cellX = -1;
+               cellY = y < 0 ? 0 : y;
+       }
+
+       /** Sets the docking side. -1 means no docking.<br>
+        * Valid sides are: <code> north = 0, west = 1, south = 2, east = 3</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current side.
+        */
+       public int getDockSide()
+       {
+               return dock;
+       }
+
+       /** Sets the docking side. -1 means no docking.<br>
+        * Valid sides are: <code> north = 0, west = 1, south = 2, east = 3</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param side -1 or 0-3.
+        */
+       public void setDockSide(int side)
+       {
+               if (side < -1 || side > 3)
+                       throw new IllegalArgumentException("Illegal dock side: " + side);
+               dock = side;
+       }
+
+       /** Returns if this component should have its bounds handled by an external source and not this layout manager.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public boolean isExternal()
+       {
+               return external;
+       }
+
+       /** If this boolean is true this component is not handled in any way by the layout manager and the component can have its bounds set by an external
+        * handler which is normally by the use of some <code>component.setBounds(x, y, width, height)</code> directly (for Swing).
+        * <p>
+        * The bounds <b>will not</b> affect the minimum and preferred size of the container.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> means that the bounds are not changed.
+        */
+       public void setExternal(boolean b)
+       {
+               this.external = b;
+       }
+
+       /** Returns if the flow in the <b>cell</b> is in the horizontal dimension. Vertical if <code>false</code>. Only the first
+        * component is a cell can set the flow.
+        * <p>
+        * If <code>null</code> the flow direction is inherited by from the {@link net.miginfocom.layout.LC}.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public Boolean getFlowX()
+       {
+               return flowX;
+       }
+
+       /** Sets if the flow in the <b>cell</b> is in the horizontal dimension. Vertical if <code>false</code>. Only the first
+        * component is a cell can set the flow.
+        * <p>
+        * If <code>null</code> the flow direction is inherited by from the {@link net.miginfocom.layout.LC}.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>Boolean.TRUE</code> means horizontal flow in the cell.
+        */
+       public void setFlowX(Boolean b)
+       {
+               this.flowX = b;
+       }
+
+       /** Sets how a component that is hidden (not visible) should be treated by default.
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The mode:<br>
+        * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+        * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+        * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+        * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+        */
+       public int getHideMode()
+       {
+               return hideMode;
+       }
+
+       /** Sets how a component that is hidden (not visible) should be treated by default.
+        * @param mode The mode:<br>
+        * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+        * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+        * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+        * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+        */
+       public void setHideMode(int mode)
+       {
+               if (mode < -1 || mode > 3)
+                       throw new IllegalArgumentException("Wrong hideMode: " + mode);
+
+               hideMode = mode;
+       }
+
+       /** Returns the id used to reference this component in some constraints.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The id or <code>null</code>. May consist of a groupID and an componentID which are separated by a dot: ".". E.g. "grp1.id1".
+        * The dot should never be first or last if present.
+        */
+       public String getId()
+       {
+               return id;
+       }
+
+       /** Sets the id used to reference this component in some constraints.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param id The id or <code>null</code>. May consist of a groupID and an componentID which are separated by a dot: ".". E.g. "grp1.id1".
+        * The dot should never be first or last if present.
+        */
+       public void setId(String id)
+       {
+               this.id = id;
+       }
+
+       /** Returns the absolute resizing in the last stage of the layout cycle. May be <code>null</code> and elements may be <code>null</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value. <code>null</code> or of length 4.
+        */
+       public UnitValue[] getPadding()
+       {
+               return padding != null ? new UnitValue[] {padding[0], padding[1], padding[2], padding[3]} : null;
+       }
+
+       /** Sets the absolute resizing in the last stage of the layout cycle. These values are added to the edges and can thus for
+        * instance be used to grow or reduce the size or move the component an absolute number of pixels. May be <code>null</code>
+        * and elements may be <code>null</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param sides top, left, bottom right. Must be <code>null</code> or of length 4.
+        */
+       public void setPadding(UnitValue[] sides)
+       {
+               this.padding = sides != null ? new UnitValue[] {sides[0], sides[1], sides[2], sides[3]} : null;
+       }
+
+       /** Returns the visual padding used when laying out this Component. May be <code>null</code> and elements may be <code>null</code>.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value. <code>null</code> or of length 4.
+        */
+       public UnitValue[] getVisualPadding()
+       {
+               return visualPadding != null ? new UnitValue[] {visualPadding[0], visualPadding[1], visualPadding[2], visualPadding[3]} : null;
+       }
+
+       /** Sets the visual padding used when laying out this Component.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param sides top, left, bottom right. Must be <code>null</code> or of length 4.
+        */
+       public void setVisualPadding(UnitValue[] sides)
+       {
+               this.visualPadding = sides != null ? new UnitValue[] {sides[0], sides[1], sides[2], sides[3]} : null;
+       }
+
+       /** Returns how many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to.
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value. 0 if no skip.
+        */
+       public int getSkip()
+       {
+               return skip;
+       }
+
+       /** Sets how many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to.
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param cells How many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to
+        */
+       public void setSkip(int cells)
+       {
+               this.skip = cells;
+       }
+
+       /** Returns the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+        * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public int getSpanX()
+       {
+               return spanX;
+       }
+
+       /** Sets the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+        * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param cells The number of cells to span (i.e. merge).
+        */
+       public void setSpanX(int cells)
+       {
+               this.spanX = cells;
+       }
+
+       /** Returns the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+        * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public int getSpanY()
+       {
+               return spanY;
+       }
+
+       /** Sets the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+        * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param cells The number of cells to span (i.e. merge).
+        */
+       public void setSpanY(int cells)
+       {
+               this.spanY = cells;
+       }
+
+       /** "pushx" indicates that the column that this component is in (this first if the component spans) should default to growing.
+        * If any other column has been set to grow this push value on the component does nothing as the column's explicit grow weight
+        * will take precedence. Push is normally used when the grid has not been defined in the layout.
+        * <p>
+        * If multiple components in a column has push weights set the largest one will be used for the column.
+        * @return The current push value. Default is <code>null</code>.
+        */
+       public Float getPushX()
+       {
+               return pushX;
+       }
+
+       /** "pushx" indicates that the column that this component is in (this first if the component spans) should default to growing.
+        * If any other column has been set to grow this push value on the component does nothing as the column's explicit grow weight
+        * will take precedence. Push is normally used when the grid has not been defined in the layout.
+        * <p>
+        * If multiple components in a column has push weights set the largest one will be used for the column.
+        * @param weight The new push value. Default is <code>null</code>.
+        */
+       public void setPushX(Float weight)
+       {
+               this.pushX = weight;
+       }
+
+       /** "pushx" indicates that the row that this component is in (this first if the component spans) should default to growing.
+        * If any other row has been set to grow this push value on the component does nothing as the row's explicit grow weight
+        * will take precedence. Push is normally used when the grid has not been defined in the layout.
+        * <p>
+        * If multiple components in a row has push weights set the largest one will be used for the row.
+        * @return The current push value. Default is <code>null</code>.
+        */
+       public Float getPushY()
+       {
+               return pushY;
+       }
+
+       /** "pushx" indicates that the row that this component is in (this first if the component spans) should default to growing.
+        * If any other row has been set to grow this push value on the component does nothing as the row's explicit grow weight
+        * will take precedence. Push is normally used when the grid has not been defined in the layout.
+        * <p>
+        * If multiple components in a row has push weights set the largest one will be used for the row.
+        * @param weight The new push value. Default is <code>null</code>.
+        */
+       public void setPushY(Float weight)
+       {
+               this.pushY = weight;
+       }
+
+       /** Returns in how many parts the current cell (that this constraint's component will be in) should be split in. If for instance
+        * it is split in two, the next component will also share the same cell. Note that the cell can also span a number of
+        * cells, which means that you can for instance span three cells and split that big cell for two components. Split can be
+        * set to a very high value to make all components in the same row/column share the same cell (e.g. <code>LayoutUtil.INF</code>).
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public int getSplit()
+       {
+               return split;
+       }
+
+       /** Sets in how many parts the current cell (that this constraint's component will be in) should be split in. If for instance
+        * it is split in two, the next component will also share the same cell. Note that the cell can also span a number of
+        * cells, which means that you can for instance span three cells and split that big cell for two components. Split can be
+        * set to a very high value to make all components in the same row/column share the same cell (e.g. <code>LayoutUtil.INF</code>).
+        * <p>
+        * Note that only the first component will be checked for this property.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param parts The number of parts (i.e. component slots) the cell should be divided into.
+        */
+       public void setSplit(int parts)
+       {
+               this.split = parts;
+       }
+
+       /** Tags the component with metadata. Currently only used to tag buttons with for instance "cancel" or "ok" to make them
+        * show up in the correct order depending on platform. See {@link PlatformDefaults#setButtonOrder(String)} for information.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value. May be <code>null</code>.
+        */
+       public String getTag()
+       {
+               return tag;
+       }
+
+       /** Optional tag that gives more context to this constraint's component. It is for instance used to tag buttons in a
+        * button bar with the button type such as "ok", "help" or "cancel".
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param tag The new tag. May be <code>null</code>.
+        */
+       public void setTag(String tag)
+       {
+               this.tag = tag;
+       }
+
+       /** Returns if the flow should wrap to the next line/column <b>after</b> the component that this constraint belongs to.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public boolean isWrap()
+       {
+               return wrap != null;
+       }
+
+       /** Sets if the flow should wrap to the next line/column <b>after</b> the component that this constraint belongs to.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> means wrap after.
+        */
+       public void setWrap(boolean b)
+       {
+               wrap = b ? (wrap == null ? DEF_GAP : wrap) : null;
+       }
+
+       /** Returns the wrap size if it is a custom size. If wrap was set to true with {@link #setWrap(boolean)} then this method will
+        * return <code>null</code> since that means that the gap size should be the default one as defined in the rows spec.
+        * @return The custom gap size. NOTE! Will return <code>null</code> for both no wrap <b>and</b> default wrap.
+        * @see #isWrap()
+        * @see #setWrap(boolean)
+        * @since 2.4.2
+        */
+       public BoundSize getWrapGapSize()
+       {
+               return wrap == DEF_GAP ? null : wrap;
+       }
+
+       /** Set the wrap size and turns wrap on if <code>!= null</code>.
+        * @param s The custom gap size. NOTE! <code>null</code> will not turn on or off wrap, it will only set the wrap gap size to "default".
+        * A non-null value will turn on wrap though.
+        * @see #isWrap()
+        * @see #setWrap(boolean)
+        * @since 2.4.2
+        */
+       public void setWrapGapSize(BoundSize s)
+       {
+               wrap = s == null ? (wrap != null ? DEF_GAP : null) : s;
+       }
+
+       /** Returns if the flow should wrap to the next line/column <b>before</b> the component that this constraint belongs to.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current value.
+        */
+       public boolean isNewline()
+       {
+               return newline != null;
+       }
+
+       /** Sets if the flow should wrap to the next line/column <b>before</b> the component that this constraint belongs to.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> means wrap before.
+        */
+       public void setNewline(boolean b)
+       {
+               newline = b ? (newline == null ? DEF_GAP : newline) : null;
+       }
+
+       /** Returns the newline size if it is a custom size. If newline was set to true with {@link #setNewline(boolean)} then this method will
+        * return <code>null</code> since that means that the gap size should be the default one as defined in the rows spec.
+        * @return The custom gap size. NOTE! Will return <code>null</code> for both no newline <b>and</b> default newline.
+        * @see #isNewline()
+        * @see #setNewline(boolean)
+        * @since 2.4.2
+        */
+       public BoundSize getNewlineGapSize()
+       {
+               return newline == DEF_GAP ? null : newline;
+       }
+
+       /** Set the newline size and turns newline on if <code>!= null</code>.
+        * @param s The custom gap size. NOTE! <code>null</code> will not turn on or off newline, it will only set the newline gap size to "default".
+        * A non-null value will turn on newline though.
+        * @see #isNewline()
+        * @see #setNewline(boolean)
+        * @since 2.4.2
+        */
+       public void setNewlineGapSize(BoundSize s)
+       {
+               newline = s == null ? (newline != null ? DEF_GAP : null) : s;
+       }
+
+       /** Returns the animation spec. Default is a spec where animation is off (prio 0).
+        * @return Never null.
+        */
+       public AnimSpec getAnimSpec()
+       {
+               return animSpec;
+       }
+
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       @Override
+       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+
+       @Override
+       public void writeExternal(ObjectOutput out) throws IOException
+       {
+               if (getClass() == CC.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+}
\ No newline at end of file
diff --git a/srcjar/net/miginfocom/layout/ComponentWrapper.java b/srcjar/net/miginfocom/layout/ComponentWrapper.java
new file mode 100644 (file)
index 0000000..7a97297
--- /dev/null
@@ -0,0 +1,305 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A class that wraps the important parts of a Component.
+ * <p>
+ * <b>NOTE!</b>.equals() and .hashcode() should be forwarded to the wrapped component. E.g.
+ * <pre>
+ *     public int hashCode()
+       {
+               return getComponent().hashCode();
+       }
+
+       public final boolean equals(Object o)
+       {
+                if (o instanceof ComponentWrapper == false)
+                        return false;
+
+                return getComponent().equals(((ComponentWrapper) o).getComponent());
+       }
+ * </pre>
+ */
+public interface ComponentWrapper
+{
+       static final int TYPE_UNSET = -1;
+       public static final int TYPE_UNKNOWN = 0;
+       public static final int TYPE_CONTAINER = 1;
+       public static final int TYPE_LABEL = 2;
+       public static final int TYPE_TEXT_FIELD = 3;
+       public static final int TYPE_TEXT_AREA = 4;
+       public static final int TYPE_BUTTON = 5;
+       public static final int TYPE_LIST = 6;
+       public static final int TYPE_TABLE = 7;
+       public static final int TYPE_SCROLL_PANE = 8;
+       public static final int TYPE_IMAGE = 9;
+       public static final int TYPE_PANEL = 10;
+       public static final int TYPE_COMBO_BOX = 11;
+       public static final int TYPE_SLIDER = 12;
+       public static final int TYPE_SPINNER = 13;
+       public static final int TYPE_PROGRESS_BAR = 14;
+       public static final int TYPE_TREE = 15;
+       public static final int TYPE_CHECK_BOX = 16;
+       public static final int TYPE_SCROLL_BAR = 17;
+       public static final int TYPE_SEPARATOR = 18;
+       public static final int TYPE_TABBED_PANE = 19;
+
+       /** Returns the actual object that this wrapper is aggregating. This might be needed for getting
+        * information about the object that the wrapper interface does not provide.
+        * <p>
+        * If this is a container the container should be returned instead.
+        * @return The actual object that this wrapper is aggregating. Not <code>null</code>.
+        */
+       public abstract Object getComponent();
+
+       /** Returns the current x coordinate for this component.
+        * @return The current x coordinate for this component.
+        */
+       public abstract int getX();
+
+       /** Returns the current y coordinate for this component.
+        * @return The current y coordinate for this component.
+        */
+       public abstract int getY();
+
+       /** Returns the current width for this component.
+        * @return The current width for this component.
+        */
+       public abstract int getWidth();
+
+       /** Returns the current height for this component.
+        * @return The current height for this component.
+        */
+       public abstract int getHeight();
+
+       /** Returns the screen x-coordinate for the upper left coordinate of the component layout-able bounds.
+        * @return The screen x-coordinate for the upper left coordinate of the component layout-able bounds.
+        */
+       public abstract int getScreenLocationX();
+
+       /** Returns the screen y-coordinate for the upper left coordinate of the component layout-able bounds.
+        * @return The screen y-coordinate for the upper left coordinate of the component layout-able bounds.
+        */
+       public abstract int getScreenLocationY();
+
+       /** Returns the minimum width of the component.
+        * @param hHint The Size hint for the other dimension. An implementation can use this value or the
+        * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+        * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+        * @return The minimum width of the component.
+        * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+        * any implementing classes. This change was worth it though.
+        */
+       public abstract int getMinimumWidth(int hHint);
+
+       /** Returns the minimum height of the component.
+        * @param wHint The Size hint for the other dimension. An implementation can use this value or the
+        * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+        * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+        * @return The minimum height of the component.
+        * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+        * any implementing classes. This change was worth it though.
+        */
+       public abstract int getMinimumHeight(int wHint);
+
+       /** Returns the preferred width of the component.
+        * @param hHint The Size hint for the other dimension. An implementation can use this value or the
+        * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+        * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+        * @return The preferred width of the component.
+        * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+        * any implementing classes. This change was worth it though.
+        */
+       public abstract int getPreferredWidth(int hHint);
+
+       /** Returns the preferred height of the component.
+        * @param wHint The Size hint for the other dimension. An implementation can use this value or the
+        * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+        * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+        * @return The preferred height of the component.
+        * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+        * any implementing classes. This change was worth it though.
+        */
+       public abstract int getPreferredHeight(int wHint);
+
+       /** Returns the maximum width of the component.
+        * @param hHint The Size hint for the other dimension. An implementation can use this value or the
+        * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+        * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+        * @return The maximum width of the component.
+        * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+        * any implementing classes. This change was worth it though.
+        */
+       public abstract int getMaximumWidth(int hHint);
+
+       /** Returns the maximum height of the component.
+        * @param wHint The Size hint for the other dimension. An implementation can use this value or the
+        * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+        * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+        * @return The maximum height of the component.
+        * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+        * any implementing classes. This change was worth it though.
+        */
+       public abstract int getMaximumHeight(int wHint);
+
+       /** Sets the component's bounds.
+        * @param x The x coordinate.
+        * @param y The y coordinate.
+        * @param width The width.
+        * @param height The height.
+        */
+       public abstract void setBounds(int x, int y, int width, int height);
+
+       /** Returns if the component's visibility is set to <code>true</code>. This should not return if the component is
+        * actually visible, but if the visibility is set to true or not.
+        * @return <code>true</code> means visible.
+        */
+       public abstract boolean isVisible();
+
+       /** Returns the baseline for the component given the suggested height.
+        * @param width The width to calculate for if other than the current. If <code>-1</code> the current size should be used.
+        * @param height The height to calculate for if other than the current. If <code>-1</code> the current size should be used.
+        * @return The baseline from the top or -1 if not applicable.
+        */
+       public abstract int getBaseline(int width, int height);
+
+       /** Returns if the component has a baseline and if it can be retrieved. Should for instance return
+        * <code>false</code> for Swing before mustang.
+        * @return If the component has a baseline and if it can be retrieved.
+        */
+       public abstract boolean hasBaseline();
+
+       /** Returns the container for this component.
+        * @return The container for this component. Will return <code>null</code> if the component has no parent.
+        */
+       public abstract ContainerWrapper getParent();
+
+       /** Returns the pixel unit factor for the horizontal or vertical dimension.
+        * <p>
+        * The factor is 1 for both dimensions on the normal font in a JPanel on Windows. The factor should increase with a bigger "X".
+        * <p>
+        * This is the Swing version:
+        * <pre>
+        * Rectangle2D r = fm.getStringBounds("X", parent.getGraphics());
+        * wFactor = r.getWidth() / 6;
+        * hFactor = r.getHeight() / 13.27734375f;
+        * </pre>
+        * @param isHor If it is the horizontal factor that should be returned.
+        * @return The factor.
+        */
+       public abstract float getPixelUnitFactor(boolean isHor);
+
+       /** Returns the DPI (Dots Per Inch) of the screen the component is currently in or for the default
+        * screen if the component is not visible.
+        * <p>
+        * If headless mode {@link net.miginfocom.layout.PlatformDefaults#getDefaultDPI} will be returned.
+        * @return The DPI.
+        */
+       public abstract int getHorizontalScreenDPI();
+
+       /** Returns the DPI (Dots Per Inch) of the screen the component is currently in or for the default
+        * screen if the component is not visible.
+        * <p>
+        * If headless mode {@link net.miginfocom.layout.PlatformDefaults#getDefaultDPI} will be returned.
+        * @return The DPI.
+        */
+       public abstract int getVerticalScreenDPI();
+
+       /** Returns the pixel size of the screen that the component is currently in or for the default
+        * screen if the component is not visible or <code>null</code>.
+        * <p>
+        * If in headless mode <code>1024</code> is returned.
+        * @return The screen size. E.g. <code>1280</code>.
+        */
+       public abstract int getScreenWidth();
+
+       /** Returns the pixel size of the screen that the component is currently in or for the default
+        * screen if the component is not visible or <code>null</code>.
+        * <p>
+        * If in headless mode <code>768</code> is returned.
+        * @return The screen size. E.g. <code>1024</code>.
+        */
+       public abstract int getScreenHeight();
+
+       /** Returns a String id that can be used to reference the component in link constraints. This value should
+        * return the default id for the component. The id can be set for a component in the constraints and if
+        * so the value returned by this method will never be used. If there are no sensible id for the component
+        * <code>null</code> should be returned.
+        * <p>
+        * For instance the Swing implementation returns the string returned from <code>Component.getName()</code>.
+        * @return The string link id or <code>null</code>.
+        */
+       public abstract String getLinkId();
+
+       /** Returns a hash code that should be reasonably different for anything that might change the layout. This value is used to
+        *  know if the component layout needs to clear any caches.
+        * @return A hash code that should be reasonably different for anything that might change the layout. Returns -1 if the widget is
+        * disposed.
+        */
+       public abstract int getLayoutHashCode();
+
+       /** Returns the padding on a component by component basis. This method can be overridden to return padding to compensate for example for
+        * borders that have shadows or where the outer most pixel is not the visual "edge" to align to.
+        * <p>
+        * Default implementation returns <code>null</code> for all components except for Windows XP's JTabbedPane which will return new Insets(0, 0, 2, 2).
+        * <p>
+        * <b>NOTE!</B> To reduce generated garbage the returned padding should never be changed so that the same insets can be returned many times.
+        * @return <code>null</code> if no padding. <b>NOTE!</B> To reduce generated garbage the returned padding should never be changed so that
+        * the same insets can be returned many times. [top, left, bottom, right]
+        */
+       public int[] getVisualPadding();
+
+       /** Paints component outline to indicate where it is.
+        * @param showVisualPadding If the visual padding should be shown in the debug drawing.
+        */
+       public abstract void paintDebugOutline(boolean showVisualPadding);
+
+       /** Returns the type of component that this wrapper is wrapping.
+        * <p>
+        * This method can be invoked often so the result should be cached.
+        * <p>
+        * @param disregardScrollPane Is <code>true</code> any wrapping scroll pane should be disregarded and the type
+        * of the scrolled component should be returned.
+        * @return The type of component that this wrapper is wrapping. E.g. {@link #TYPE_LABEL}.
+        */
+       public abstract int getComponentType(boolean disregardScrollPane);
+
+       /** Returns in what way the min/pref/max sizes relates to it's height or width for the current settings of the component (like wrapText).
+        * If the min/pref/max height depends on it's width return {@link net.miginfocom.layout.LayoutUtil#HORIZONTAL}
+        * If the min/pref/max width depends on it's height (not common) return {@link net.miginfocom.layout.LayoutUtil#VERTICAL}
+        * If there is no connection between the preferred min/pref/max and the size of the component return -1.
+        * @since 5.0
+        */
+       public abstract int getContentBias();
+}
\ No newline at end of file
diff --git a/srcjar/net/miginfocom/layout/ConstraintParser.java b/srcjar/net/miginfocom/layout/ConstraintParser.java
new file mode 100644 (file)
index 0000000..f819fde
--- /dev/null
@@ -0,0 +1,1481 @@
+package net.miginfocom.layout;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** Parses string constraints.
+ */
+public final class ConstraintParser
+{
+       private ConstraintParser()
+       {
+       }
+
+       /** Parses the layout constraints and stores the parsed values in the transient (cache) member variables.
+        * @param s The String to parse. Should not be <code>null</code> and <b>must be lower case and trimmed</b>.
+        * @throws RuntimeException if the constraint was not valid.
+        * @return The parsed constraint. Never <code>null</code>.
+        */
+       public static LC parseLayoutConstraint(String s)
+       {
+               LC lc = new LC();
+               if (s.isEmpty())
+                       return lc;
+
+               String[] parts = toTrimmedTokens(s, ',');
+
+               // First check for "ltr" or "rtl" since that will affect the interpretation of the other constraints.
+               for (int i = 0; i < parts.length; i++) {
+                       String part = parts[i];
+                       if (part == null)
+                               continue;
+
+                       int len = part.length();
+                       if (len == 3 || len == 11) {   // Optimization
+                               if (part.equals("ltr") || part.equals("rtl") || part.equals("lefttoright") || part.equals("righttoleft")) {
+                                       lc.setLeftToRight(part.charAt(0) == 'l' ? Boolean.TRUE : Boolean.FALSE);
+                                       parts[i] = null;    // So we will not try to interpret it again
+                               }
+
+                               if (part.equals("ttb") || part.equals("btt") || part.equals("toptobottom") || part.equals("bottomtotop")) {
+                                       lc.setTopToBottom(part.charAt(0) == 't');
+                                       parts[i] = null;    // So we will not try to interpret it again
+                               }
+                       }
+               }
+
+               for (String part : parts) {
+                       if (part == null || part.length() == 0)
+                               continue;
+
+                       try {
+                               int ix = -1;
+                               char c = part.charAt(0);
+
+                               if (c == 'w' || c == 'h') {
+
+                                       ix = startsWithLenient(part, "wrap", -1, true);
+                                       if (ix > -1) {
+                                               String num = part.substring(ix).trim();
+                                               lc.setWrapAfter(num.length() != 0 ? Integer.parseInt(num) : 0);
+                                               continue;
+                                       }
+
+                                       boolean isHor = c == 'w';
+                                       if (isHor && (part.startsWith("w ") || part.startsWith("width "))) {
+                                               String sz = part.substring(part.charAt(1) == ' ' ? 2 : 6).trim();
+                                               lc.setWidth(parseBoundSize(sz, false, true));
+                                               continue;
+                                       }
+
+                                       if (!isHor && (part.startsWith("h ") || part.startsWith("height "))) {
+                                               String uvStr = part.substring(part.charAt(1) == ' ' ? 2 : 7).trim();
+                                               lc.setHeight(parseBoundSize(uvStr, false, false));
+                                               continue;
+                                       }
+
+                                       if (part.length() > 5) {
+                                               String sz = part.substring(5).trim();
+                                               if (part.startsWith("wmin ")) {
+                                                       lc.minWidth(sz);
+                                                       continue;
+                                               } else if (part.startsWith("wmax ")) {
+                                                       lc.maxWidth(sz);
+                                                       continue;
+                                               } else if (part.startsWith("hmin ")) {
+                                                       lc.minHeight(sz);
+                                                       continue;
+                                               } else if (part.startsWith("hmax ")) {
+                                                       lc.maxHeight(sz);
+                                                       continue;
+                                               }
+                                       }
+
+                                       if (part.startsWith("hidemode ")) {
+                                               lc.setHideMode(Integer.parseInt(part.substring(9)));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'g') {
+                                       if (part.startsWith("gapx ")) {
+                                               lc.setGridGapX(parseBoundSize(part.substring(5).trim(), true, true));
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("gapy ")) {
+                                               lc.setGridGapY(parseBoundSize(part.substring(5).trim(), true, false));
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("gap ")) {
+                                               String[] gaps = toTrimmedTokens(part.substring(4).trim(), ' ');
+                                               lc.setGridGapX(parseBoundSize(gaps[0], true, true));
+                                               lc.setGridGapY(gaps.length > 1 ? parseBoundSize(gaps[1], true, false) : lc.getGridGapX());
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'd') {
+                                       ix = startsWithLenient(part, "debug", 5, true);
+                                       if (ix > -1) {
+                                               String millis = part.substring(ix).trim();
+                                               lc.setDebugMillis(millis.length() > 0 ? Integer.parseInt(millis) : 1000);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'n') {
+                                       if (part.equals("nogrid")) {
+                                               lc.setNoGrid(true);
+                                               continue;
+                                       }
+
+                                       if (part.equals("nocache")) {
+                                               lc.setNoCache(true);
+                                               continue;
+                                       }
+
+                                       if (part.equals("novisualpadding")) {
+                                               lc.setVisualPadding(false);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'f') {
+                                       if (part.equals("fill") || part.equals("fillx") || part.equals("filly")) {
+                                               lc.setFillX(part.length() == 4 || part.charAt(4) == 'x');
+                                               lc.setFillY(part.length() == 4 || part.charAt(4) == 'y');
+                                               continue;
+                                       }
+
+                                       if (part.equals("flowy")) {
+                                               lc.setFlowX(false);
+                                               continue;
+                                       }
+
+                                       if (part.equals("flowx")) {
+                                               lc.setFlowX(true); // This is the default but added for consistency
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'i') {
+                                       ix = startsWithLenient(part, "insets", 3, true);
+                                       if (ix > -1) {
+                                               String insStr = part.substring(ix).trim();
+                                               UnitValue[] ins = parseInsets(insStr, true);
+                                               LayoutUtil.putCCString(ins, insStr);
+                                               lc.setInsets(ins);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'a') {
+                                       ix = startsWithLenient(part, new String[]{"aligny", "ay"}, new int[]{6, 2}, true);
+                                       if (ix > -1) {
+                                               UnitValue align = parseUnitValueOrAlign(part.substring(ix).trim(), false, null);
+                                               if (align == UnitValue.BASELINE_IDENTITY)
+                                                       throw new IllegalArgumentException("'baseline' can not be used to align the whole component group.");
+                                               lc.setAlignY(align);
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"alignx", "ax"}, new int[]{6, 2}, true);
+                                       if (ix > -1) {
+                                               lc.setAlignX(parseUnitValueOrAlign(part.substring(ix).trim(), true, null));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "align", 2, true);
+                                       if (ix > -1) {
+                                               String[] gaps = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               lc.setAlignX(parseUnitValueOrAlign(gaps[0], true, null));
+                                               if (gaps.length > 1) {
+                                                       UnitValue align = parseUnitValueOrAlign(gaps[1], false, null);
+                                                       if (align == UnitValue.BASELINE_IDENTITY)
+                                                               throw new IllegalArgumentException("'baseline' can not be used to align the whole component group.");
+                                                       lc.setAlignY(align);
+                                               }
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'p') {
+                                       if (part.startsWith("packalign ")) {
+                                               String[] packs = toTrimmedTokens(part.substring(10).trim(), ' ');
+                                               lc.setPackWidthAlign(packs[0].length() > 0 ? Float.parseFloat(packs[0]) : 0.5f);
+                                               if (packs.length > 1)
+                                                       lc.setPackHeightAlign(Float.parseFloat(packs[1]));
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("pack ") || part.equals("pack")) {
+                                               String ps = part.substring(4).trim();
+                                               String[] packs = toTrimmedTokens(ps.length() > 0 ? ps : "pref pref", ' ');
+                                               lc.setPackWidth(parseBoundSize(packs[0], false, true));
+                                               if (packs.length > 1)
+                                                       lc.setPackHeight(parseBoundSize(packs[1], false, false));
+
+                                               continue;
+                                       }
+                               }
+
+                               if (lc.getAlignX() == null) {
+                                       UnitValue alignX = parseAlignKeywords(part, true);
+                                       if (alignX != null) {
+                                               lc.setAlignX(alignX);
+                                               continue;
+                                       }
+                               }
+
+                               UnitValue alignY = parseAlignKeywords(part, false);
+                               if (alignY != null) {
+                                       lc.setAlignY(alignY);
+                                       continue;
+                               }
+
+                               throw new IllegalArgumentException("Unknown Constraint: '" + part + "'\n");
+
+                       } catch (Exception ex) {
+                               throw new IllegalArgumentException("Illegal Constraint: '" + part + "'\n" + ex.getMessage());
+                       }
+               }
+
+//             lc = (LC) serializeTest(lc);
+
+               return lc;
+       }
+
+       /** Parses the column or rows constraints. They normally looks something like <code>"[min:pref]rel[10px][]"</code>.
+        * @param s The string to parse. Not <code>null</code>.
+        * @return An array of {@link DimConstraint}s that is as many are there exist "[...]" sections in the string that is parsed.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       public static AC parseRowConstraints(String s)
+       {
+               return parseAxisConstraint(s, false);
+       }
+
+       /** Parses the column or rows constraints. They normally looks something like <code>"[min:pref]rel[10px][]"</code>.
+        * @param s The string to parse. Not <code>null</code>.
+        * @return An array of {@link DimConstraint}s that is as many are there exist "[...]" sections in the string that is parsed.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       public static AC parseColumnConstraints(String s)
+       {
+               return parseAxisConstraint(s, true);
+       }
+
+       /** Parses the column or rows constraints. They normally looks something like <code>"[min:pref]rel[10px][]"</code>.
+        * @param s The string to parse. Not <code>null</code>.
+        * @param isCols If this for columns rather than rows.
+        * @return An array of {@link DimConstraint}s that is as many are there exist "[...]" sections in the string that is parsed.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       private static AC parseAxisConstraint(String s, boolean isCols)
+       {
+               s = s.trim();
+
+               if (s.length() == 0)
+                       return new AC();    // Short circuit for performance.
+
+               s = s.toLowerCase();
+
+               ArrayList<String> parts = getRowColAndGapsTrimmed(s);
+
+               BoundSize[] gaps = new BoundSize[(parts.size() >> 1) + 1];
+               for (int i = 0, iSz = parts.size(), gIx = 0; i < iSz; i += 2, gIx++)
+                       gaps[gIx] = parseBoundSize(parts.get(i), true, isCols);
+
+               DimConstraint[] colSpecs = new DimConstraint[parts.size() >> 1];
+               for (int i = 0, gIx = 0; i < colSpecs.length; i++, gIx++) {
+                       if (gIx >= gaps.length - 1)
+                               gIx = gaps.length - 2;
+
+                       colSpecs[i] = parseDimConstraint(parts.get((i << 1) + 1), gaps[gIx], gaps[gIx + 1], isCols);
+               }
+
+               AC ac = new AC();
+               ac.setConstaints(colSpecs);
+
+//             ac = (AC) serializeTest(ac);
+
+               return ac;
+       }
+
+       /** Parses a single column or row constraint.
+        * @param s The single constraint to parse. May look something like <code>"min:pref,fill,grow"</code>. Should not be <code>null</code> and <b>must
+        * be lower case and trimmed</b>.
+        * @param gapBefore The default gap "before" the column/row constraint. Can be overridden with a <code>"gap"</code> section within <code>s</code>.
+        * @param gapAfter The default gap "after" the column/row constraint. Can be overridden with a <code>"gap"</code> section within <code>s</code>.
+        * @param isCols If the constraints are column constraints rather than row constraints.
+        * @return A single constraint. Never <code>null</code>.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       private static DimConstraint parseDimConstraint(String s, BoundSize gapBefore, BoundSize gapAfter, boolean isCols)
+       {
+               DimConstraint dimConstraint = new DimConstraint();
+
+               // Default values.
+               dimConstraint.setGapBefore(gapBefore);
+               dimConstraint.setGapAfter(gapAfter);
+
+               String[] parts = toTrimmedTokens(s, ',');
+               for (int i = 0; i < parts.length; i++) {
+                       String part = parts[i];
+                       try {
+                               if (part.length() == 0)
+                                       continue;
+
+                               if (part.equals("fill")) {
+                                       dimConstraint.setFill(true);
+//                                      dimConstraint.setAlign(null);   // Can not have both fill and alignment (changed for 3.5 since it can have "growy 0")
+                                       continue;
+                               }
+
+                               if (part.equals("nogrid")) {
+                                       dimConstraint.setNoGrid(true);
+                                       continue;
+                               }
+
+                               int ix = -1;
+                               char c = part.charAt(0);
+
+                               if (c == 's') {
+                                       ix = startsWithLenient(part, new String[] {"sizegroup", "sg"}, new int[] {5, 2}, true);
+                                       if (ix > -1) {
+                                               dimConstraint.setSizeGroup(part.substring(ix).trim());
+                                               continue;
+                                       }
+
+
+                                       ix = startsWithLenient(part, new String[] {"shrinkprio", "shp"}, new int[] {10, 3}, true);
+                                       if (ix > -1) {
+                                               dimConstraint.setShrinkPriority(Integer.parseInt(part.substring(ix).trim()));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "shrink", 6, true);
+                                       if (ix > -1) {
+                                               dimConstraint.setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'g') {
+                                       ix = startsWithLenient(part, new String[] {"growpriority", "gp"}, new int[] {5, 2}, true);
+                                       if (ix > -1) {
+                                               dimConstraint.setGrowPriority(Integer.parseInt(part.substring(ix).trim()));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "grow", 4, true);
+                                       if (ix > -1) {
+                                               dimConstraint.setGrow(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'a') {
+                                       ix = startsWithLenient(part, "align", 2, true);
+                                       if (ix > -1) {
+//                                             if (dimConstraint.isFill() == false)    // Swallow, but ignore if fill is set. (changed for 3.5 since it can have "growy 0")
+                                                       dimConstraint.setAlign(parseUnitValueOrAlign(part.substring(ix).trim(), isCols, null));
+                                               continue;
+                                       }
+                               }
+
+                               UnitValue align = parseAlignKeywords(part, isCols);
+                               if (align != null) {
+//                                     if (dimConstraint.isFill() == false)    // Swallow, but ignore if fill is set. (changed for 3.5 since it can have "growy 0")
+                                               dimConstraint.setAlign(align);
+                                       continue;
+                               }
+
+                                // Only min:pref:max still left that is ok
+                               dimConstraint.setSize(parseBoundSize(part, false, isCols));
+
+                       } catch (Exception ex) {
+                               throw new IllegalArgumentException("Illegal constraint: '" + part + "'\n" + ex.getMessage());
+                       }
+               }
+               return dimConstraint;
+       }
+
+       /** Parses all component constraints and stores the parsed values in the transient (cache) member variables.
+        * @param constrMap The constraints as <code>String</code>s. Strings <b>must be lower case and trimmed</b>
+        * @return The parsed constraints. Never <code>null</code>.
+        */
+       public static Map<ComponentWrapper, CC> parseComponentConstraints(Map<ComponentWrapper, String> constrMap)
+       {
+               HashMap<ComponentWrapper, CC> flowConstrMap = new HashMap<ComponentWrapper, CC>();
+
+               for (Iterator<Map.Entry<ComponentWrapper, String>> it = constrMap.entrySet().iterator(); it.hasNext();) {
+                       Map.Entry<ComponentWrapper, String> entry = it.next();
+                       flowConstrMap.put(entry.getKey(), parseComponentConstraint(entry.getValue()));
+               }
+
+               return flowConstrMap;
+       }
+
+       /** Parses one component constraint and returns the parsed value.
+        * @param s The string to parse. <b>Must be lower case and trimmed</b>.
+        * @throws RuntimeException if the constraint was not valid.
+        * @return The parsed constraint. Never <code>null</code>.
+        */
+       public static CC parseComponentConstraint(String s)
+       {
+               CC cc = new CC();
+
+               if (s == null || s.isEmpty())
+                       return cc;
+
+               String[] parts = toTrimmedTokens(s, ',');
+
+               for (String part : parts) {
+                       try {
+                               if (part.length() == 0)
+                                       continue;
+
+                               int ix = -1;
+                               char c = part.charAt(0);
+
+                               if (c == 'n') {
+                                       if (part.equals("north")) {
+                                               cc.setDockSide(0);
+                                               continue;
+                                       }
+
+                                       if (part.equals("newline")) {
+                                               cc.setNewline(true);
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("newline ")) {
+                                               String gapSz = part.substring(7).trim();
+                                               cc.setNewlineGapSize(parseBoundSize(gapSz, true, true));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'f' && (part.equals("flowy") || part.equals("flowx"))) {
+                                       cc.setFlowX(part.charAt(4) == 'x' ? Boolean.TRUE : Boolean.FALSE);
+                                       continue;
+                               }
+
+                               if (c == 's') {
+                                       ix = startsWithLenient(part, "skip", 4, true);
+                                       if (ix > -1) {
+                                               String num = part.substring(ix).trim();
+                                               cc.setSkip(num.length() != 0 ? Integer.parseInt(num) : 1);
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "split", 5, true);
+                                       if (ix > -1) {
+                                               String split = part.substring(ix).trim();
+                                               cc.setSplit(split.length() > 0 ? Integer.parseInt(split) : LayoutUtil.INF);
+                                               continue;
+                                       }
+
+                                       if (part.equals("south")) {
+                                               cc.setDockSide(2);
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"spany", "sy"}, new int[]{5, 2}, true);
+                                       if (ix > -1) {
+                                               cc.setSpanY(parseSpan(part.substring(ix).trim()));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"spanx", "sx"}, new int[]{5, 2}, true);
+                                       if (ix > -1) {
+                                               cc.setSpanX(parseSpan(part.substring(ix).trim()));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "span", 4, true);
+                                       if (ix > -1) {
+                                               String[] spans = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               cc.setSpanX(spans[0].length() > 0 ? Integer.parseInt(spans[0]) : LayoutUtil.INF);
+                                               cc.setSpanY(spans.length > 1 ? Integer.parseInt(spans[1]) : 1);
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "shrinkx", 7, true);
+                                       if (ix > -1) {
+                                               cc.getHorizontal().setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "shrinky", 7, true);
+                                       if (ix > -1) {
+                                               cc.getVertical().setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "shrink", 6, false);
+                                       if (ix > -1) {
+                                               String[] shrinks = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               cc.getHorizontal().setShrink(parseFloat(shrinks[0], ResizeConstraint.WEIGHT_100));
+                                               if (shrinks.length > 1)
+                                                       cc.getVertical().setShrink(parseFloat(shrinks[1], ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"shrinkprio", "shp"}, new int[]{10, 3}, true);
+                                       if (ix > -1) {
+                                               String sp = part.substring(ix).trim();
+                                               if (sp.startsWith("x") || sp.startsWith("y")) { // To handle "gpx", "gpy", "shrinkpriorityx", shrinkpriorityy"
+                                                       (sp.startsWith("x") ? cc.getHorizontal() : cc.getVertical()).setShrinkPriority(Integer.parseInt(sp.substring(2)));
+                                               } else {
+                                                       String[] shrinks = toTrimmedTokens(sp, ' ');
+                                                       cc.getHorizontal().setShrinkPriority(Integer.parseInt(shrinks[0]));
+                                                       if (shrinks.length > 1)
+                                                               cc.getVertical().setShrinkPriority(Integer.parseInt(shrinks[1]));
+                                               }
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"sizegroupx", "sizegroupy", "sgx", "sgy"}, new int[]{9, 9, 2, 2}, true);
+                                       if (ix > -1) {
+                                               String sg = part.substring(ix).trim();
+                                               char lc = part.charAt(ix - 1);
+                                               if (lc != 'y')
+                                                       cc.getHorizontal().setSizeGroup(sg);
+                                               if (lc != 'x')
+                                                       cc.getVertical().setSizeGroup(sg);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'g') {
+                                       ix = startsWithLenient(part, "growx", 5, true);
+                                       if (ix > -1) {
+                                               cc.getHorizontal().setGrow(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "growy", 5, true);
+                                       if (ix > -1) {
+                                               cc.getVertical().setGrow(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "grow", 4, false);
+                                       if (ix > -1) {
+                                               String[] grows = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               cc.getHorizontal().setGrow(parseFloat(grows[0], ResizeConstraint.WEIGHT_100));
+                                               cc.getVertical().setGrow(parseFloat(grows.length > 1 ? grows[1] : "", ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"growprio", "gp"}, new int[]{8, 2}, true);
+                                       if (ix > -1) {
+                                               String gp = part.substring(ix).trim();
+                                               char c0 = gp.length() > 0 ? gp.charAt(0) : ' ';
+                                               if (c0 == 'x' || c0 == 'y') { // To handle "gpx", "gpy", "growpriorityx", growpriorityy"
+                                                       (c0 == 'x' ? cc.getHorizontal() : cc.getVertical()).setGrowPriority(Integer.parseInt(gp.substring(2)));
+                                               } else {
+                                                       String[] grows = toTrimmedTokens(gp, ' ');
+                                                       cc.getHorizontal().setGrowPriority(Integer.parseInt(grows[0]));
+                                                       if (grows.length > 1)
+                                                               cc.getVertical().setGrowPriority(Integer.parseInt(grows[1]));
+                                               }
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("gap")) {
+                                               BoundSize[] gaps = parseGaps(part); // Changes order!!
+                                               if (gaps[0] != null)
+                                                       cc.getVertical().setGapBefore(gaps[0]);
+                                               if (gaps[1] != null)
+                                                       cc.getHorizontal().setGapBefore(gaps[1]);
+                                               if (gaps[2] != null)
+                                                       cc.getVertical().setGapAfter(gaps[2]);
+                                               if (gaps[3] != null)
+                                                       cc.getHorizontal().setGapAfter(gaps[3]);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'a') {
+                                       ix = startsWithLenient(part, new String[]{"aligny", "ay"}, new int[]{6, 2}, true);
+                                       if (ix > -1) {
+                                               cc.getVertical().setAlign(parseUnitValueOrAlign(part.substring(ix).trim(), false, null));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"alignx", "ax"}, new int[]{6, 2}, true);
+                                       if (ix > -1) {
+                                               cc.getHorizontal().setAlign(parseUnitValueOrAlign(part.substring(ix).trim(), true, null));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "align", 2, true);
+                                       if (ix > -1) {
+                                               String[] gaps = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               cc.getHorizontal().setAlign(parseUnitValueOrAlign(gaps[0], true, null));
+                                               if (gaps.length > 1)
+                                                       cc.getVertical().setAlign(parseUnitValueOrAlign(gaps[1], false, null));
+                                               continue;
+                                       }
+                               }
+
+                               if ((c == 'x' || c == 'y') && part.length() > 2) {
+                                       char c2 = part.charAt(1);
+                                       if (c2 == ' ' || (c2 == '2' && part.charAt(2) == ' ')) {
+                                               if (cc.getPos() == null) {
+                                                       cc.setPos(new UnitValue[4]);
+                                               } else if (cc.isBoundsInGrid() == false) {
+                                                       throw new IllegalArgumentException("Cannot combine 'position' with 'x/y/x2/y2' keywords.");
+                                               }
+
+                                               int edge = (c == 'x' ? 0 : 1) + (c2 == '2' ? 2 : 0);
+                                               UnitValue[] pos = cc.getPos();
+                                               pos[edge] = parseUnitValue(part.substring(2).trim(), null, c == 'x');
+                                               cc.setPos(pos);
+                                               cc.setBoundsInGrid(true);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'c') {
+                                       ix = startsWithLenient(part, "cell", 4, true);
+                                       if (ix > -1) {
+                                               String[] grs = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               if (grs.length < 2)
+                                                       throw new IllegalArgumentException("At least two integers must follow " + part);
+                                               cc.setCellX(Integer.parseInt(grs[0]));
+                                               cc.setCellY(Integer.parseInt(grs[1]));
+                                               if (grs.length > 2)
+                                                       cc.setSpanX(Integer.parseInt(grs[2]));
+                                               if (grs.length > 3)
+                                                       cc.setSpanY(Integer.parseInt(grs[3]));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'p') {
+                                       ix = startsWithLenient(part, "pos", 3, true);
+                                       if (ix > -1) {
+                                               if (cc.getPos() != null && cc.isBoundsInGrid())
+                                                       throw new IllegalArgumentException("Can not combine 'pos' with 'x/y/x2/y2' keywords.");
+
+                                               String[] pos = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               UnitValue[] bounds = new UnitValue[4];
+                                               for (int j = 0; j < pos.length; j++)
+                                                       bounds[j] = parseUnitValue(pos[j], null, j % 2 == 0);
+
+                                               if (bounds[0] == null && bounds[2] == null || bounds[1] == null && bounds[3] == null)
+                                                       throw new IllegalArgumentException("Both x and x2 or y and y2 can not be null!");
+
+                                               cc.setPos(bounds);
+                                               cc.setBoundsInGrid(false);
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "pad", 3, true);
+                                       if (ix > -1) {
+                                               UnitValue[] p = parseInsets(part.substring(ix).trim(), false);
+                                               cc.setPadding(new UnitValue[]{
+                                                       p[0],
+                                                       p.length > 1 ? p[1] : null,
+                                                       p.length > 2 ? p[2] : null,
+                                                       p.length > 3 ? p[3] : null});
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "pushx", 5, true);
+                                       if (ix > -1) {
+                                               cc.setPushX(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "pushy", 5, true);
+                                       if (ix > -1) {
+                                               cc.setPushY(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, "push", 4, false);
+                                       if (ix > -1) {
+                                               String[] pushs = toTrimmedTokens(part.substring(ix).trim(), ' ');
+                                               cc.setPushX(parseFloat(pushs[0], ResizeConstraint.WEIGHT_100));
+                                               cc.setPushY(parseFloat(pushs.length > 1 ? pushs[1] : "", ResizeConstraint.WEIGHT_100));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 't') {
+                                       ix = startsWithLenient(part, "tag", 3, true);
+                                       if (ix > -1) {
+                                               cc.setTag(part.substring(ix).trim());
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'w' || c == 'h') {
+                                       if (part.equals("wrap")) {
+                                               cc.setWrap(true);
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("wrap ")) {
+                                               String gapSz = part.substring(5).trim();
+                                               cc.setWrapGapSize(parseBoundSize(gapSz, true, true));
+                                               continue;
+                                       }
+
+                                       boolean isHor = c == 'w';
+                                       if (isHor && (part.startsWith("w ") || part.startsWith("width "))) {
+                                               String uvStr = part.substring(part.charAt(1) == ' ' ? 2 : 6).trim();
+                                               cc.getHorizontal().setSize(parseBoundSize(uvStr, false, true));
+                                               continue;
+                                       }
+
+                                       if (!isHor && (part.startsWith("h ") || part.startsWith("height "))) {
+                                               String uvStr = part.substring(part.charAt(1) == ' ' ? 2 : 7).trim();
+                                               cc.getVertical().setSize(parseBoundSize(uvStr, false, false));
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("wmin ") || part.startsWith("wmax ") || part.startsWith("hmin ") || part.startsWith("hmax ")) {
+                                               String uvStr = part.substring(5).trim();
+                                               if (uvStr.length() > 0) {
+                                                       UnitValue uv = parseUnitValue(uvStr, null, isHor);
+                                                       boolean isMin = part.charAt(3) == 'n';
+                                                       DimConstraint dc = isHor ? cc.getHorizontal() : cc.getVertical();
+                                                       dc.setSize(new BoundSize(
+                                                               isMin ? uv : dc.getSize().getMin(),
+                                                               dc.getSize().getPreferred(),
+                                                               isMin ? (dc.getSize().getMax()) : uv,
+                                                               uvStr
+                                                       ));
+                                                       continue;
+                                               }
+                                       }
+
+                                       if (part.equals("west")) {
+                                               cc.setDockSide(1);
+                                               continue;
+                                       }
+
+                                       if (part.startsWith("hidemode ")) {
+                                               cc.setHideMode(Integer.parseInt(part.substring(9)));
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'i' && part.startsWith("id ")) {
+                                       cc.setId(part.substring(3).trim());
+                                       int dIx = cc.getId().indexOf('.');
+                                       if (dIx == 0 || dIx == cc.getId().length() - 1)
+                                               throw new IllegalArgumentException("Dot must not be first or last!");
+
+                                       continue;
+                               }
+
+                               if (c == 'e') {
+                                       if (part.equals("east")) {
+                                               cc.setDockSide(3);
+                                               continue;
+                                       }
+
+                                       if (part.equals("external")) {
+                                               cc.setExternal(true);
+                                               continue;
+                                       }
+
+                                       ix = startsWithLenient(part, new String[]{"endgroupx", "endgroupy", "egx", "egy"}, new int[]{-1, -1, -1, -1}, true);
+                                       if (ix > -1) {
+                                               String sg = part.substring(ix).trim();
+                                               char lc = part.charAt(ix - 1);
+                                               DimConstraint dc = (lc == 'x' ? cc.getHorizontal() : cc.getVertical());
+                                               dc.setEndGroup(sg);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'd') {
+                                       if (part.equals("dock north")) {
+                                               cc.setDockSide(0);
+                                               continue;
+                                       }
+                                       if (part.equals("dock west")) {
+                                               cc.setDockSide(1);
+                                               continue;
+                                       }
+                                       if (part.equals("dock south")) {
+                                               cc.setDockSide(2);
+                                               continue;
+                                       }
+                                       if (part.equals("dock east")) {
+                                               cc.setDockSide(3);
+                                               continue;
+                                       }
+
+                                       if (part.equals("dock center")) {
+                                               cc.getHorizontal().setGrow(100f);
+                                               cc.getVertical().setGrow(100f);
+                                               cc.setPushX(100f);
+                                               cc.setPushY(100f);
+                                               continue;
+                                       }
+                               }
+
+                               if (c == 'v') {
+                                       ix = startsWithLenient(part, new String[] {"visualpadding", "vp"}, new int[] {3, 2}, true);
+                                       if (ix > -1) {
+                                               UnitValue[] p = parseInsets(part.substring(ix).trim(), false);
+                                               cc.setVisualPadding(new UnitValue[] {
+                                                       p[0],
+                                                       p.length > 1 ? p[1] : null,
+                                                       p.length > 2 ? p[2] : null,
+                                                       p.length > 3 ? p[3] : null});
+                                               continue;
+                                       }
+                               }
+
+                               UnitValue horAlign = parseAlignKeywords(part, true);
+                               if (horAlign != null) {
+                                       cc.getHorizontal().setAlign(horAlign);
+                                       continue;
+                               }
+
+                               UnitValue verAlign = parseAlignKeywords(part, false);
+                               if (verAlign != null) {
+                                       cc.getVertical().setAlign(verAlign);
+                                       continue;
+                               }
+
+                               throw new IllegalArgumentException("Unknown keyword.");
+
+                       } catch (Exception ex) {
+                               throw new IllegalArgumentException("Error parsing Constraint: '" + part + "'", ex);
+                       }
+               }
+
+//             cc = (CC) serializeTest(cc);
+
+               return cc;
+       }
+
+       /** Parses insets which consists of 1-4 <code>UnitValue</code>s.
+        * @param s The string to parse. E.g. "10 10 10 10" or "20". If less than 4 groups the last will be used for the missing.
+        * @param acceptPanel If "panel" and "dialog" should be accepted. They are used to access platform defaults.
+        * @return An array of length 4 with the parsed insets.
+        * @throws IllegalArgumentException if the parsing could not be done.
+        */
+       public static UnitValue[] parseInsets(String s, boolean acceptPanel)
+       {
+               if (s.length() == 0 || s.equals("dialog") || s.equals("panel")) {
+                       if (acceptPanel == false)
+                               throw new IllegalArgumentException("Insets now allowed: " + s + "\n");
+
+                       boolean isPanel = s.startsWith("p");
+                       UnitValue[] ins = new UnitValue[4];
+                       for (int j = 0; j < 4; j++)
+                               ins[j] = isPanel ? PlatformDefaults.getPanelInsets(j) : PlatformDefaults.getDialogInsets(j);
+
+                       return ins;
+               } else {
+                       String[] insS = toTrimmedTokens(s, ' ');
+                       UnitValue[] ins = new UnitValue[4];
+                       for (int j = 0; j < 4; j++) {
+                               UnitValue insSz = parseUnitValue(insS[j < insS.length ? j : insS.length - 1], UnitValue.ZERO, j % 2 == 1);
+                               ins[j] = insSz != null ? insSz : PlatformDefaults.getPanelInsets(j);
+                       }
+                       return ins;
+               }
+       }
+
+       /** Parses gaps.
+        * @param s The string that contains gap information. Should start with "gap".
+        * @return The gaps as specified in <code>s</code>. Indexed: <code>[top,left,bottom,right][min,pref,max]</code> or
+        * [before,after][min,pref,max] if <code>oneDim</code> is true.
+        */
+       private static BoundSize[] parseGaps(String s)
+       {
+               BoundSize[] ret = new BoundSize[4];
+
+               int ix = startsWithLenient(s, "gaptop", -1, true);
+               if (ix > -1) {
+                       s = s.substring(ix).trim();
+                       ret[0] = parseBoundSize(s, true, false);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, "gapleft", -1, true);
+               if (ix > -1) {
+                       s = s.substring(ix).trim();
+                       ret[1] = parseBoundSize(s, true, true);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, "gapbottom", -1, true);
+               if (ix > -1) {
+                       s = s.substring(ix).trim();
+                       ret[2] = parseBoundSize(s, true, false);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, "gapright", -1, true);
+               if (ix > -1) {
+                       s = s.substring(ix).trim();
+                       ret[3] = parseBoundSize(s, true, true);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, "gapbefore", -1, true);
+               if (ix > -1) {
+                       s = s.substring(ix).trim();
+                       ret[1] = parseBoundSize(s, true, true);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, "gapafter", -1, true);
+               if (ix > -1) {
+                       s = s.substring(ix).trim();
+                       ret[3] = parseBoundSize(s, true, true);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, new String[] {"gapx", "gapy"}, null, true);
+               if (ix > -1) {
+                       boolean x = s.charAt(3) == 'x';
+                       String[] gaps = toTrimmedTokens(s.substring(ix).trim(), ' ');
+                       ret[x ? 1 : 0] = parseBoundSize(gaps[0], true, x);
+                       if (gaps.length > 1)
+                               ret[x ? 3 : 2] = parseBoundSize(gaps[1], true, !x);
+                       return ret;
+               }
+
+               ix = startsWithLenient(s, "gap ", 1, true);
+               if (ix > -1) {
+                       String[] gaps = toTrimmedTokens(s.substring(ix).trim(), ' ');
+
+                       ret[1] = parseBoundSize(gaps[0], true, true);   // left
+                       if (gaps.length > 1) {
+                               ret[3] = parseBoundSize(gaps[1], true, false);   // right
+                               if (gaps.length > 2) {
+                                       ret[0] = parseBoundSize(gaps[2], true, true);   // top
+                                       if (gaps.length > 3)
+                                               ret[2] = parseBoundSize(gaps[3], true, false);   // bottom
+                               }
+                       }
+                       return ret;
+               }
+
+               throw new IllegalArgumentException("Unknown Gap part: '" + s + "'");
+       }
+
+       private static int parseSpan(String s)
+       {
+               return s.length() > 0 ? Integer.parseInt(s) : LayoutUtil.INF;
+       }
+
+       private static Float parseFloat(String s, Float nullVal)
+       {
+               return s.length() > 0 ? new Float(Float.parseFloat(s)) : nullVal;
+       }
+
+       /** Parses a single "min:pref:max" value. May look something like <code>"10px:20lp:30%"</code> or <code>"pref!"</code>.
+        * @param s The string to parse. Not <code>null</code>.
+        * @param isGap If this bound size is a gap (different empty string handling).
+        * @param isHor If the size is for the horizontal dimension.
+        * @return A bound size that may be <code>null</code> if the string was "null", "n" or <code>null</code>.
+        */
+       public static BoundSize parseBoundSize(String s, boolean isGap, boolean isHor)
+       {
+               if (s.length() == 0 || s.equals("null") || s.equals("n"))
+                       return null;
+
+               String cs = s;
+               boolean push = false;
+               if (s.endsWith("push")) {
+                       push = true;
+                       int l = s.length();
+                       s = s.substring(0, l - (s.endsWith(":push") ? 5 : 4));
+                       if (s.length() == 0)
+                               return new BoundSize(null, null, null, true, cs);
+               }
+
+               String[] sizes = toTrimmedTokens(s, ':');
+               String s0 = sizes[0];
+
+               if (sizes.length == 1) {
+                       boolean hasEM = s0.endsWith("!");
+                       if (hasEM)
+                               s0 = s0.substring(0, s0.length() - 1);
+                       UnitValue uv = parseUnitValue(s0, null, isHor);
+                       return new BoundSize(((isGap || hasEM) ? uv : null), uv, (hasEM ? uv : null), push, cs);
+
+               } else if (sizes.length == 2) {
+                       return new BoundSize(parseUnitValue(s0, null, isHor), parseUnitValue(sizes[1], null, isHor), null, push, cs);
+               } else if (sizes.length == 3) {
+                       return new BoundSize(parseUnitValue(s0, null, isHor), parseUnitValue(sizes[1], null, isHor), parseUnitValue(sizes[2], null, isHor), push, cs);
+               } else {
+                       throw new IllegalArgumentException("Min:Preferred:Max size section must contain 0, 1 or 2 colons. '" + cs + "'");
+               }
+       }
+
+       /** Parses a single unit value that may also be an alignment as parsed by {@link #parseAlignKeywords(String, boolean)}.
+        * @param s The string to parse. Not <code>null</code>. May look something like <code>"10px"</code> or <code>"5dlu"</code>.
+        * @param isHor If the value is for the horizontal dimension.
+        * @param emptyReplacement A replacement if <code>s</code> is empty. May be <code>null</code>.
+        * @return The parsed unit value. May be <code>null</code>.
+        */
+       public static UnitValue parseUnitValueOrAlign(String s, boolean isHor, UnitValue emptyReplacement)
+       {
+               if (s.length() == 0)
+                       return emptyReplacement;
+
+               UnitValue align = parseAlignKeywords(s, isHor);
+               if (align != null)
+                       return align;
+
+               return parseUnitValue(s, emptyReplacement, isHor);
+       }
+
+       /** Parses a single unit value. E.g. "10px" or "5in"
+        * @param s The string to parse. Not <code>null</code>. May look something like <code>"10px"</code> or <code>"5dlu"</code>.
+        * @param isHor If the value is for the horizontal dimension.
+        * @return The parsed unit value. <code>null</code> is empty string,
+        */
+       public static UnitValue parseUnitValue(String s, boolean isHor)
+       {
+               return parseUnitValue(s, null, isHor);
+       }
+
+       /** Parses a single unit value.
+        * @param s The string to parse. May be <code>null</code>. May look something like <code>"10px"</code> or <code>"5dlu"</code>.
+        * @param emptyReplacement A replacement <code>s</code> is empty or <code>null</code>. May be <code>null</code>.
+        * @param isHor If the value is for the horizontal dimension.
+        * @return The parsed unit value. May be <code>null</code>.
+        */
+       private static UnitValue parseUnitValue(String s, UnitValue emptyReplacement, boolean isHor)
+       {
+               if (s == null || s.length() == 0)
+                       return emptyReplacement;
+
+               String cs = s; // Save creation string.
+               char c0 = s.charAt(0);
+
+               // Remove start and end parentheses, if there.
+               if (c0 == '(' && s.charAt(s.length() - 1) == ')')
+                       s = s.substring(1, s.length() - 1);
+
+               if (c0 == 'n' && (s.equals("null") || s.equals("n")))
+                       return null;
+
+               if (c0 == 'i' && s.equals("inf"))
+                       return UnitValue.INF;
+
+               int oper = getOper(s);
+               boolean inline = oper == UnitValue.ADD || oper == UnitValue.SUB || oper == UnitValue.MUL || oper == UnitValue.DIV;
+
+               if (oper != UnitValue.STATIC) {  // It is a multi-value
+
+                       String[] uvs;
+                       if (inline == false) {   // If the format is of type "opr(xxx,yyy)" (compared to in-line "10%+15px")
+                               String sub = s.substring(4, s.length() - 1).trim();
+                               uvs = toTrimmedTokens(sub, ',');
+                               if (uvs.length == 1)
+                                       return parseUnitValue(sub, null, isHor);
+                       } else {
+                               char delim;
+                               if (oper == UnitValue.ADD) {
+                                       delim = '+';
+                               } else if (oper == UnitValue.SUB) {
+                                       delim = '-';
+                               } else if (oper == UnitValue.MUL) {
+                                       delim = '*';
+                               } else {    // div left
+                                       delim = '/';
+                               }
+                               uvs = toTrimmedTokens(s, delim);
+                               if (uvs.length > 2) {   // More than one +-*/.
+                                       String last = uvs[uvs.length - 1];
+                                       String first = s.substring(0, s.length() - last.length() - 1);
+                                       uvs = new String[] {first, last};
+                               }
+                       }
+
+                       if (uvs.length != 2)
+                               throw new IllegalArgumentException("Malformed UnitValue: '" + s + "'");
+
+                       UnitValue sub1 = parseUnitValue(uvs[0], null, isHor);
+                       UnitValue sub2 = parseUnitValue(uvs[1], null, isHor);
+
+                       if (sub1 == null || sub2 == null)
+                               throw new IllegalArgumentException("Malformed UnitValue. Must be two sub-values: '" + s + "'");
+
+                       return new UnitValue(isHor, oper, sub1, sub2, cs);
+               } else {
+                       try {
+                               String[] numParts = getNumTextParts(s);
+                               float value = numParts[0].length() > 0 ? Float.parseFloat(numParts[0]) : 1;     // e.g. "related" has no number part..
+
+                               return new UnitValue(value, numParts[1], isHor, oper, cs);
+
+                       } catch(Exception e) {
+                               throw new IllegalArgumentException("Malformed UnitValue: '" + s + "'", e);
+                       }
+               }
+       }
+
+       /** Parses alignment keywords and returns the appropriate <code>UnitValue</code>.
+        * @param s The string to parse. Not <code>null</code>.
+        * @param isHor If alignments for horizontal is checked. <code>false</code> means vertical.
+        * @return The unit value or <code>null</code> if not recognized (no exception).
+        */
+       static UnitValue parseAlignKeywords(String s, boolean isHor)
+       {
+               if (startsWithLenient(s, "center", 1, false) != -1)
+                       return UnitValue.CENTER;
+
+               if (isHor) {
+                       if (startsWithLenient(s, "left", 1, false) != -1)
+                               return UnitValue.LEFT;
+
+                       if (startsWithLenient(s, "right", 1, false) != -1)
+                               return UnitValue.RIGHT;
+
+                       if (startsWithLenient(s, "leading", 4, false) != -1)
+                               return UnitValue.LEADING;
+
+                       if (startsWithLenient(s, "trailing", 5, false) != -1)
+                               return UnitValue.TRAILING;
+
+                       if (startsWithLenient(s, "label", 5, false) != -1)
+                               return UnitValue.LABEL;
+
+               } else {
+
+                       if (startsWithLenient(s, "baseline", 4, false) != -1)
+                               return UnitValue.BASELINE_IDENTITY;
+
+                       if (startsWithLenient(s, "top", 1, false) != -1)
+                               return UnitValue.TOP;
+
+                       if (startsWithLenient(s, "bottom", 1, false) != -1)
+                               return UnitValue.BOTTOM;
+               }
+
+               return null;
+       }
+
+       /** Splits a text-number combination such as "hello 10.0" into <code>{"hello", "10.0"}</code>.
+        * @param s The string to split. Not <code>null</code>. Needs be be reasonably formatted since the method
+        * only finds the first 0-9 or . and cuts the string in half there.
+        * @return Always length 2 and no <code>null</code> elements. Elements are "" if no part found.
+        */
+       private static String[] getNumTextParts(String s)
+       {
+               for (int i = 0, iSz = s.length(); i < iSz; i++) {
+                       char c = s.charAt(i);
+                       if (c == ' ')
+                               throw new IllegalArgumentException("Space in UnitValue: '" + s + "'");
+
+                       if ((c < '0' || c > '9') && c != '.' && c != '-')
+                               return new String[] {s.substring(0, i).trim(), s.substring(i).trim()};
+               }
+               return new String[] {s, ""};
+       }
+
+       /** Returns the operation depending on the start character.
+        * @param s The string to check. Not <code>null</code>.
+        * @return E.g. UnitValue.ADD, UnitValue.SUB or UnitValue.STATIC. Returns negative value for in-line operations.
+        */
+       private static int getOper(String s)
+       {
+               int len = s.length();
+               if (len < 3)
+                       return UnitValue.STATIC;
+
+               if (len > 5 && s.charAt(3) == '(' && s.charAt(len - 1) == ')') {
+                       if (s.startsWith("min("))
+                               return UnitValue.MIN;
+
+                       if (s.startsWith("max("))
+                               return UnitValue.MAX;
+
+                       if (s.startsWith("mid("))
+                               return UnitValue.MID;
+               }
+
+               // Try in-line add/sub. E.g. "pref+10px".
+               for (int j = 0; j < 2; j++) {   // First +-   then */   (precedence)
+                       for (int i = len - 1, p = 0; i > 0; i--) {
+                               char c = s.charAt(i);
+                               if (c == ')') {
+                                       p++;
+                               } else if (c == '(') {
+                                       p--;
+                               } else if (p == 0) {
+                                       if (j == 0) {
+                                               if (c == '+')
+                                                       return UnitValue.ADD;
+                                               if (c == '-')
+                                                       return UnitValue.SUB;
+                                       } else {
+                                               if (c == '*')
+                                                       return UnitValue.MUL;
+                                               if (c == '/')
+                                                       return UnitValue.DIV;
+                                       }
+                               }
+                       }
+               }
+               return UnitValue.STATIC;
+       }
+
+       /** Returns if a string shares at least a specified numbers starting characters with a number of matches.
+        * <p>
+        * This method just exercise {@link #startsWithLenient(String, String, int, boolean)} with every one of
+        * <code>matches</code> and <code>minChars</code>.
+        * @param s The string to check. Not <code>null</code>.
+        * @param matches A number of possible starts for <code>s</code>.
+        * @param minChars The minimum number of characters to match for every element in <code>matches</code>. Needs
+        * to be of same length as <code>matches</code>. Can be <code>null</code>.
+        * @param acceptTrailing If after the required number of characters are matched on recognized characters that are not
+        * in one of the the <code>matches</code> string should be accepted. For instance if "abczz" should be matched with
+        * "abcdef" and min chars 3.
+        * @return The index of the first unmatched character if <code>minChars</code> was reached or <code>-1</code> if a match was not
+        * found.
+        */
+       private static int startsWithLenient(String s, String[] matches, int[] minChars, boolean acceptTrailing)
+       {
+               for (int i = 0; i < matches.length; i++) {
+                       int minChar = minChars != null ? minChars[i] : -1;
+                       int ix = startsWithLenient(s, matches[i], minChar, acceptTrailing);
+                       if (ix > -1)
+                               return ix;
+               }
+               return -1;
+       }
+
+       /** Returns if a string shares at least a specified numbers starting characters with a match.
+        * @param s The string to check. Not <code>null</code> and must be trimmed.
+        * @param match The possible start for <code>s</code>. Not <code>null</code> and must be trimmed.
+        * @param minChars The mimimum number of characters to match to <code>s</code> for it this to be considered a match. -1 means
+        * the full length of <code>match</code>.
+        * @param acceptTrailing If after the required number of charecters are matched unrecognized characters that are not
+        * in one of the the <code>matches</code> string should be accepted. For instance if "abczz" should be matched with
+        * "abcdef" and min chars 3.
+        * @return The index of the first unmatched character if <code>minChars</code> was reached or <code>-1</code> if a match was not
+        * found.
+        */
+       private static int startsWithLenient(String s, String match, int minChars, boolean acceptTrailing)
+       {
+               if (s.charAt(0) != match.charAt(0)) // Fast sanity check.
+                       return -1;
+
+               if (minChars == -1)
+                       minChars = match.length();
+
+               int sSz = s.length();
+               if (sSz < minChars)
+                       return -1;
+
+               int mSz = match.length();
+               int sIx = 0;
+               for (int mIx = 0; mIx < mSz; sIx++, mIx++) {
+                       while (sIx < sSz && (s.charAt(sIx) == ' ' || s.charAt(sIx) == '_'))    // Disregard spaces and _
+                               sIx++;
+
+                       if (sIx >= sSz || s.charAt(sIx) != match.charAt(mIx))
+                               return mIx >= minChars && (acceptTrailing || sIx >= sSz) && (sIx >= sSz || s.charAt(sIx - 1) == ' ') ? sIx : -1;
+               }
+               return sIx >= sSz || acceptTrailing ||s.charAt(sIx) == ' ' ? sIx : -1;
+       }
+
+       /** Parses a string and returns it in those parts of the string that are separated with a <code>sep</code> character.
+        * <p>
+        * separator characters within parentheses will not be counted or handled in any way, whatever the depth.
+        * <p>
+        * A space separator will be a hit to one or more spaces and thus not return empty strings.
+        * @param s The string to parse. If it starts and/or ends with a <code>sep</code> the first and/or last element returned will be "". If
+        * two <code>sep</code> are next to each other and empty element will be "between" the periods. The <code>sep</code> themselves will never be returned.
+        * @param sep The separator char.
+        * @return Those parts of the string that are separated with <code>sep</code>. Never null and at least of size 1
+        * @since 6.7.2 Changed so more than one space in a row works as one space.
+        */
+       private static String[] toTrimmedTokens(String s, char sep)
+       {
+               int toks = 0, sSize = s.length();
+               boolean disregardDoubles = sep == ' ';
+
+               // Count the sep:s
+               int p = 0;
+               for(int i = 0; i < sSize; i++) {
+                       char c = s.charAt(i);
+                       if (c == '(') {
+                               p++;
+                       } else if (c == ')') {
+                               p--;
+                       } else if (p == 0 && c == sep) {
+                               toks++;
+                               while (disregardDoubles && i < sSize - 1 && s.charAt(i + 1) == ' ')
+                                       i++;
+                       }
+                       if (p < 0)
+                               throw new IllegalArgumentException("Unbalanced parentheses: '" + s + "'");
+               }
+               if (p != 0)
+                       throw new IllegalArgumentException("Unbalanced parentheses: '" + s + "'");
+
+               if (toks == 0)
+                       return new String [] {s.trim()};
+
+               String[] retArr = new String[toks + 1];
+
+               int st = 0, pNr = 0;
+               p = 0;
+               for (int i = 0; i < sSize; i++) {
+
+                       char c = s.charAt(i);
+                       if (c == '(') {
+                               p++;
+                       } else if (c == ')') {
+                               p--;
+                       } else if (p == 0 && c == sep) {
+                               retArr[pNr++] = s.substring(st, i).trim();
+                               st = i + 1;
+                               while (disregardDoubles && i < sSize - 1 && s.charAt(i + 1) == ' ')
+                                       i++;
+                       }
+               }
+
+               retArr[pNr++] = s.substring(st, sSize).trim();
+               return retArr;
+       }
+
+       /** Parses "AAA[BBB]CCC[DDD]EEE" into {"AAA", "BBB", "CCC", "DDD", "EEE", "FFF"}. Handles empty parts. Will always start and end outside
+        * a [] block so that the number of returned elemets will always be uneven and at least of length 3.
+        * <p>
+        * "|" is interpreted as "][".
+        * @param s The string. Might be "" but not null. Should be trimmed.
+        * @return The string divided into elements. Never <code>null</code> and at least of length 3.
+        * @throws IllegalArgumentException If a [] mismatch of some kind. (If not same [ as ] count or if the interleave.)
+        */
+       private static ArrayList<String> getRowColAndGapsTrimmed(String s)
+       {
+               if (s.indexOf('|') != -1)
+                       s = s.replaceAll("\\|", "][");
+
+               ArrayList<String> retList = new ArrayList<String>(Math.max(s.length() >> 2 + 1, 3)); // Approx return length.
+               int s0 = 0, s1 = 0; // '[' and ']' count.
+               int st = 0; // Start of "next token to add".
+               for (int i = 0, iSz = s.length(); i < iSz; i++) {
+                       char c = s.charAt(i);
+                       if (c == '[') {
+                               s0++;
+                       } else if (c == ']') {
+                               s1++;
+                       } else {
+                               continue;
+                       }
+
+                       if (s0 != s1 && (s0 - 1) != s1)
+                               break;  // Wrong [ or ] found. Break for throw.
+
+                       retList.add(s.substring(st, i).trim());
+                       st = i + 1;
+               }
+               if (s0 != s1)
+                       throw new IllegalArgumentException("'[' and ']' mismatch in row/column format string: " + s);
+
+               if (s0 == 0) {
+                       retList.add("");
+                       retList.add(s);
+                       retList.add("");
+               } else if (retList.size() % 2 == 0) {
+                       retList.add(s.substring(st, s.length()));
+               }
+
+               return retList;
+       }
+
+       /** Makes <code>null</code> "", trims and converts to lower case.
+        * @param s The string
+        * @return Not null.
+        */
+       public static String prepare(String s)
+       {
+               return s != null ? s.trim().toLowerCase() : "";
+       }
+
+//     /** Tests to serialize and deserialize the object with both XMLEncoder/Decoder and through Serializable
+//      * @param o The object to serialize
+//      * @return The same object after a tri through the process.
+//      */
+//     public static final Object serializeTest(Object o)
+//     {
+//             try {
+//                     ByteArrayOutputStream barr = new ByteArrayOutputStream();
+//                     XMLEncoder enc = new XMLEncoder(barr);
+//                     enc.writeObject(o);
+//                     enc.close();
+//
+//                     XMLDecoder dec = new XMLDecoder(new ByteArrayInputStream(barr.toByteArray()));
+//                     o = dec.readObject();
+//                     dec.close();
+//             } catch (Exception e) {
+//                     e.printStackTrace();
+//             }
+//
+//             try {
+//                     ByteArrayOutputStream barr = new ByteArrayOutputStream();
+//                     ObjectOutputStream oos = new ObjectOutputStream(barr);
+//                     oos.writeObject(o);
+//                     oos.close();
+//
+//                     ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
+//                     o = ois.readObject();
+//                     ois.close();
+//             } catch (Exception e) {
+//                     e.printStackTrace();
+//             }
+//
+//             return o;
+//     }
+}
diff --git a/srcjar/net/miginfocom/layout/ContainerWrapper.java b/srcjar/net/miginfocom/layout/ContainerWrapper.java
new file mode 100644 (file)
index 0000000..e6b53e0
--- /dev/null
@@ -0,0 +1,69 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A class that wraps a container that contains components.
+ */
+public interface ContainerWrapper extends ComponentWrapper
+{
+       /** Returns the components of the container that wrapper is wrapping.
+        * @return The components of the container that wrapper is wrapping. Never <code>null</code>.
+        */
+       public abstract ComponentWrapper[] getComponents();
+
+       /** Returns the number of components that this parent has.
+        * @return The number of components that this parent has.
+        */
+       public abstract int getComponentCount();
+
+       /** Returns the <code>LayoutHandler</code> (in Swing terms) that is handling the layout of this container.
+        * If there exist no such class the method should return the same as {@link #getComponent()}, which is the
+        * container itself.
+        * @return The layout handler instance. Never <code>null</code>.
+        */
+       public abstract Object getLayout();
+
+       /** Returns if this container is using left-to-right component ordering.
+        * @return If this container is using left-to-right component ordering.
+        */
+       public abstract boolean isLeftToRight();
+
+       /** Paints a cell to indicate where it is.
+        * @param x The x coordinate to start the drawing.
+        * @param y The x coordinate to start the drawing.
+        * @param width The width to draw/fill
+        * @param height The height to draw/fill
+        */
+       public abstract void paintDebugCell(int x, int y, int width, int height);
+}
diff --git a/srcjar/net/miginfocom/layout/DimConstraint.java b/srcjar/net/miginfocom/layout/DimConstraint.java
new file mode 100644 (file)
index 0000000..c36d88c
--- /dev/null
@@ -0,0 +1,477 @@
+package net.miginfocom.layout;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectStreamException;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A simple value holder for a constraint for one dimension.
+ */
+public final class DimConstraint implements Externalizable
+{
+       /** How this entity can be resized in the dimension that this constraint represents.
+        */
+       final ResizeConstraint resize = new ResizeConstraint();
+
+       // Look at the properties' getter/setter methods for explanation
+
+       private String sizeGroup = null;            // A "context" compared with equals.
+
+       private BoundSize size = BoundSize.NULL_SIZE;     // Min, pref, max. Never null, but sizes can be null.
+
+       private BoundSize gapBefore = null, gapAfter = null;
+
+       private UnitValue align = null;
+
+
+       // **************  Only applicable on components! *******************
+
+       private String endGroup = null;            // A "context" compared with equals.
+
+
+       // **************  Only applicable on rows/columns! *******************
+
+       private boolean fill = false;
+
+       private boolean noGrid = false;
+
+       /** Empty constructor.
+        */
+       public DimConstraint()
+       {
+       }
+
+       /** Returns the grow priority. Relative priority is used for determining which entities gets the extra space first.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The grow priority.
+        */
+       public int getGrowPriority()
+       {
+               return resize.growPrio;
+       }
+
+       /** Sets the grow priority. Relative priority is used for determining which entities gets the extra space first.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The new grow priority.
+        */
+       public void setGrowPriority(int p)
+       {
+               resize.growPrio = p;
+       }
+
+       /** Returns the grow weight.<p>
+        * Grow weight is how flexible the entity should be, relative to other entities, when it comes to growing. <code>null</code> or
+        * zero mean it will never grow. An entity that has twice the grow weight compared to another entity will get twice
+        * as much of available space.
+        * <p>
+        * GrowWeight are only compared within the same GrowPrio.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current grow weight.
+        */
+       public Float getGrow()
+       {
+               return resize.grow;
+       }
+
+       /** Sets the grow weight.<p>
+        * Grow weight is how flexible the entity should be, relative to other entities, when it comes to growing. <code>null</code> or
+        * zero mean it will never grow. An entity that has twice the grow weight compared to another entity will get twice
+        * as much of available space.
+        * <p>
+        * GrowWeight are only compared within the same GrowPrio.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param weight The new grow weight.
+        */
+       public void setGrow(Float weight)
+       {
+               resize.grow = weight;
+       }
+
+       /** Returns the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The shrink priority.
+        */
+       public int getShrinkPriority()
+       {
+               return resize.shrinkPrio;
+       }
+
+       /** Sets the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param p The new shrink priority.
+        */
+       public void setShrinkPriority(int p)
+       {
+               resize.shrinkPrio = p;
+       }
+
+       /** Returns the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+        * Shrink weight is how flexible the entity should be, relative to other entities, when it comes to shrinking. <code>null</code> or
+        * zero mean it will never shrink (default). An entity that has twice the shrink weight compared to another entity will get twice
+        * as much of available space.
+        * <p>
+        * Shrink(Weight) are only compared within the same ShrinkPrio.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current shrink weight.
+        */
+       public Float getShrink()
+       {
+               return resize.shrink;
+       }
+
+       /** Sets the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+        * Shrink weight is how flexible the entity should be, relative to other entities, when it comes to shrinking. <code>null</code> or
+        * zero mean it will never shrink (default). An entity that has twice the shrink weight compared to another entity will get twice
+        * as much of available space.
+        * <p>
+        * Shrink(Weight) are only compared within the same ShrinkPrio.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param weight The new shrink weight.
+        */
+       public void setShrink(Float weight)
+       {
+               resize.shrink = weight;
+       }
+
+       public UnitValue getAlignOrDefault(boolean isCols)
+       {
+               if (align != null)
+                       return align;
+
+               if (isCols)
+                       return UnitValue.LEADING;
+
+               return fill || PlatformDefaults.getDefaultRowAlignmentBaseline() == false ? UnitValue.CENTER : UnitValue.BASELINE_IDENTITY;
+       }
+
+       /** Returns the alignment used either as a default value for sub-entities or for this entity.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The alignment.
+        */
+       public UnitValue getAlign()
+       {
+               return align;
+       }
+
+       /** Sets the alignment used wither as a default value for sub-entities or for this entity.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param uv The new shrink priority. E.g. {@link UnitValue#CENTER} or {@link net.miginfocom.layout.UnitValue#LEADING}.
+        */
+       public void setAlign(UnitValue uv)
+       {
+               this.align = uv;
+       }
+
+       /** Returns the gap after this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+        * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The gap after this entity
+        */
+       public BoundSize getGapAfter()
+       {
+               return gapAfter;
+       }
+
+       /** Sets the gap after this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+        * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The new gap.
+        * @see net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean) 
+        */
+       public void setGapAfter(BoundSize size)
+       {
+               this.gapAfter = size;
+       }
+
+       boolean hasGapAfter()
+       {
+               return gapAfter != null && gapAfter.isUnset() == false;
+       }
+
+       boolean isGapAfterPush()
+       {
+               return gapAfter != null && gapAfter.getGapPush();
+       }
+
+       /** Returns the gap before this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+        * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The gap before this entity
+        */
+       public BoundSize getGapBefore()
+       {
+               return gapBefore;
+       }
+
+       /** Sets the gap before this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+        * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+        * <p>
+        * See also {@link net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean)}.
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The new gap.
+        */
+       public void setGapBefore(BoundSize size)
+       {
+               this.gapBefore = size;
+       }
+
+       boolean hasGapBefore()
+       {
+               return gapBefore != null && gapBefore.isUnset() == false;
+       }
+
+       boolean isGapBeforePush()
+       {
+               return gapBefore != null && gapBefore.getGapPush();
+       }
+
+       /** Returns the min/preferred/max size for the entity in the dimension that this object describes.
+        * <p>
+     * See also {@link net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean)}.
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current size. Never <code>null</code> since v3.5.
+        */
+       public BoundSize getSize()
+       {
+               return size;
+       }
+
+       /** Sets the min/preferred/max size for the entity in the dimension that this object describes.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param size The new size. May be <code>null</code>.
+        */
+       public void setSize(BoundSize size)
+       {
+               if (size != null)
+                       size.checkNotLinked();
+               this.size = size;
+       }
+
+       /** Returns the size group that this entity should be in for the dimension that this object is describing.
+        * If this constraint is in a size group that is specified here. <code>null</code> means no size group
+        * and all other values are legal. Comparison with .equals(). Components/columns/rows in the same size group
+        * will have the same min/preferred/max size; that of the largest in the group for the first two and the
+        * smallest for max.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current size group. May be <code>null</code>.
+        */
+       public String getSizeGroup()
+       {
+               return sizeGroup;
+       }
+
+       /** Sets the size group that this entity should be in for the dimension that this object is describing.
+        * If this constraint is in a size group that is specified here. <code>null</code> means no size group
+        * and all other values are legal. Comparison with .equals(). Components/columns/rows in the same size group
+        * will have the same min/preferred/max size; that of the largest in the group for the first two and the
+        * smallest for max.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The new size group. <code>null</code> disables size grouping.
+        */
+       public void setSizeGroup(String s)
+       {
+               sizeGroup = s;
+       }
+
+       // **************  Only applicable on components ! *******************
+
+       /** Returns the end group that this entity should be in for the dimension that this object is describing.
+        * If this constraint is in an end group that is specified here. <code>null</code> means no end group
+        * and all other values are legal. Comparison with .equals(). Components in the same end group
+        * will have the same end coordinate.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return The current end group. <code>null</code> may be returned.
+        */
+       public String getEndGroup()
+       {
+               return endGroup;
+       }
+
+       /** Sets the end group that this entity should be in for the dimension that this object is describing.
+        * If this constraint is in an end group that is specified here. <code>null</code> means no end group
+        * and all other values are legal. Comparison with .equals(). Components in the same end group
+        * will have the same end coordinate.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The new end group. <code>null</code> disables end grouping.
+        */
+       public void setEndGroup(String s)
+       {
+               endGroup = s;
+       }
+
+       // **************  Not applicable on components below ! *******************
+
+       /** Returns if the component in the row/column that this constraint should default be grown in the same dimension that
+        * this constraint represents (width for column and height for a row).
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>true</code> means that components should grow.
+        */
+       public boolean isFill()
+       {
+               return fill;
+       }
+
+       /** Sets if the component in the row/column that this constraint should default be grown in the same dimension that
+        * this constraint represents (width for column and height for a row).
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> means that components should grow.
+        */
+       public void setFill(boolean b)
+       {
+               fill = b;
+       }
+
+       /** Returns if the row/column should default to flow and not to grid behaviour. This means that the whole row/column
+        * will be one cell and all components will end up in that cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>true</code> means that the whole row/column should be one cell.
+        */
+       public boolean isNoGrid()
+       {
+               return noGrid;
+       }
+
+       /** Sets if the row/column should default to flow and not to grid behaviour. This means that the whole row/column
+        * will be one cell and all components will end up in that cell.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> means that the whole row/column should be one cell.
+        */
+       public void setNoGrid(boolean b)
+       {
+               this.noGrid = b;
+       }
+
+       /** Returns the gaps as pixel values.
+        * @param parent The parent. Used to get the pixel values.
+        * @param defGap The default gap to use if there is no gap set on this object (i.e. it is null).
+        * @param refSize The reference size used to get the pixel sizes.
+        * @param before IF it is the gap before rather than the gap after to return.
+        * @return The [min,preferred,max] sizes for the specified gap. Uses {@link net.miginfocom.layout.LayoutUtil#NOT_SET}
+        * for gap sizes that are <code>null</code>. Returns <code>null</code> if there was no gap specified. A new and free to use array.
+        */
+       int[] getRowGaps(ContainerWrapper parent, BoundSize defGap, int refSize, boolean before)
+       {
+               BoundSize gap = before ? gapBefore : gapAfter;
+               if (gap == null || gap.isUnset())
+                       gap = defGap;
+
+               if (gap == null || gap.isUnset())
+                       return null;
+
+               int[] ret = new int[3];
+               for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+                       UnitValue uv = gap.getSize(i);
+                       ret[i] = uv != null ? uv.getPixels(refSize, parent, null) : LayoutUtil.NOT_SET;
+               }
+               return ret;
+       }
+
+       /** Returns the gaps as pixel values.
+        * @param parent The parent. Used to get the pixel values.
+        * @param comp The component that the gap is for. If not for a component it is <code>null</code>.
+        * @param adjGap The gap that the adjacent component, if any, has towards <code>comp</code>.
+        * @param adjacentComp The adjacent component if any. May be <code>null</code>.
+        * @param refSize The reference size used to get the pixel sizes.
+        * @param adjacentSide What side the <code>adjacentComp</code> is on. 0 = top, 1 = left, 2 = bottom, 3 = right.
+        * @param tag The tag string that the component might be tagged with in the component constraints. May be <code>null</code>.
+        * @param isLTR If it is left-to-right.
+        * @return The [min,preferred,max] sizes for the specified gap. Uses {@link net.miginfocom.layout.LayoutUtil#NOT_SET}
+        * for gap sizes that are <code>null</code>. Returns <code>null</code> if there was no gap specified. A new and free to use array.
+        */
+       int[] getComponentGaps(ContainerWrapper parent, ComponentWrapper comp, BoundSize adjGap, ComponentWrapper adjacentComp, String tag, int refSize, int adjacentSide, boolean isLTR)
+       {
+               BoundSize gap = adjacentSide < 2 ? gapBefore : gapAfter;
+
+               boolean hasGap = gap != null && gap.getGapPush();
+               if ((gap == null || gap.isUnset()) && (adjGap == null || adjGap.isUnset()) && comp != null)
+                       gap = PlatformDefaults.getDefaultComponentGap(comp, adjacentComp, adjacentSide + 1, tag, isLTR);
+
+               if (gap == null)
+                       return hasGap ? new int[] {0, 0, LayoutUtil.NOT_SET} : null;
+
+               int[] ret = new int[3];
+               for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+                       UnitValue uv = gap.getSize(i);
+                       ret[i] = uv != null ? uv.getPixels(refSize, parent, null) : LayoutUtil.NOT_SET;
+               }
+               return ret;
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       @Override
+       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+
+       @Override
+       public void writeExternal(ObjectOutput out) throws IOException
+       {
+               if (getClass() == DimConstraint.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/Grid.java b/srcjar/net/miginfocom/layout/Grid.java
new file mode 100644 (file)
index 0000000..ec56de4
--- /dev/null
@@ -0,0 +1,2496 @@
+package net.miginfocom.layout;
+
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** Holds components in a grid. Does most of the logic behind the layout manager.
+ */
+public final class Grid
+{
+       public static final boolean TEST_GAPS = true;
+
+       private static final Float[] GROW_100 = new Float[] {ResizeConstraint.WEIGHT_100};
+
+       private static final DimConstraint DOCK_DIM_CONSTRAINT = new DimConstraint();
+       static {
+               DOCK_DIM_CONSTRAINT.setGrowPriority(0);
+       }
+
+       /** This is the maximum grid position for "normal" components. Docking components use the space out to
+        * <code>MAX_DOCK_GRID</code> and below 0.
+        */
+       private static final int MAX_GRID = 30000;
+
+       /** Docking components will use the grid coordinates <code>-MAX_DOCK_GRID -> 0</code> and <code>MAX_GRID -> MAX_DOCK_GRID</code>.
+        */
+       private static final int MAX_DOCK_GRID = 32767;
+
+       /** A constraint used for gaps.
+        */
+       private static final ResizeConstraint GAP_RC_CONST = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, null);
+       private static final ResizeConstraint GAP_RC_CONST_PUSH = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, ResizeConstraint.WEIGHT_100);
+
+       /** Used for components that doesn't have a CC set. Not that it's really really important that the CC is never changed in this Grid class.
+        */
+       private static final CC DEF_CC = new CC();
+
+       /** The constraints. Never <code>null</code>.
+        */
+       private final LC lc;
+
+       /** The parent that is layout out and this grid is done for. Never <code>null</code>.
+        */
+       private final ContainerWrapper container;
+
+       /** An x, y array implemented as a sparse array to accommodate for any grid size without wasting memory (or rather 15 bit (0-MAX_GRID * 0-MAX_GRID).
+        */
+       private final LinkedHashMap<Integer, Cell> grid = new LinkedHashMap<Integer, Cell>();   // [(y << 16) + x] -> Cell. null key for absolute positioned compwraps
+
+       private HashMap<Integer, BoundSize> wrapGapMap = null;   // Row or Column index depending in the dimension that "wraps". Normally row indexes but may be column indexes if "flowy". 0 means before first row/col.
+
+       /** The size of the grid. Row count and column count.
+        */
+       private final TreeSet<Integer> rowIndexes = new TreeSet<Integer>(), colIndexes = new TreeSet<Integer>();
+
+       /** The row and column specifications.
+        */
+       private final AC rowConstr, colConstr;
+
+       /** The in the constructor calculated min/pref/max sizes of the rows and columns.
+        */
+       private FlowSizeSpec colFlowSpecs = null, rowFlowSpecs = null;
+
+       /** Components that are connections in one dimension (such as baseline alignment for instance) are grouped together and stored here.
+        * One for each row/column.
+        */
+       private final ArrayList<LinkedDimGroup>[] colGroupLists, rowGroupLists;   //[(start)row/col number]
+
+       /** The in the constructor calculated min/pref/max size of the whole grid.
+        */
+       private int[] width = null, height = null;
+
+       /** If debug is on contains the bounds for things to paint when calling {@link ContainerWrapper#paintDebugCell(int, int, int, int)}
+         */
+       private ArrayList<int[]> debugRects = null; // [x, y, width, height]
+
+       /** If any of the absolute coordinates for component bounds has links the name of the target is in this Set.
+        * Since it requires some memory and computations this is checked at the creation so that
+        * the link information is only created if needed later.
+        * <p>
+        * The boolean is true for groups id:s and null for normal id:s.
+        */
+       private HashMap<String, Boolean> linkTargetIDs = null;
+
+       private final int dockOffY, dockOffX;
+
+       private final Float[] pushXs, pushYs;
+
+       private final ArrayList<LayoutCallback> callbackList;
+
+       /** Constructor.
+        * @param container The container that will be laid out.
+        * @param lc The form flow constraints.
+        * @param rowConstr The rows specifications. If more cell rows are required, the last element will be used for when there is no corresponding element in this array.
+        * @param colConstr The columns specifications. If more cell rows are required, the last element will be used for when there is no corresponding element in this array.
+        * @param ccMap The map containing the parsed constraints for each child component of <code>parent</code>. Will not be altered. Can have null CC which will use a common
+        * cached one.
+        * @param callbackList A list of callbacks or <code>null</code> if none. Will not be altered.
+        */
+       public Grid(ContainerWrapper container, LC lc, AC rowConstr, AC colConstr, Map<? extends ComponentWrapper, CC> ccMap, ArrayList<LayoutCallback> callbackList)
+       {
+               this.lc = lc;
+               this.rowConstr = rowConstr;
+               this.colConstr = colConstr;
+               this.container = container;
+               this.callbackList = callbackList;
+
+               int wrap = lc.getWrapAfter() != 0 ? lc.getWrapAfter() : (lc.isFlowX() ? colConstr : rowConstr).getConstaints().length;
+               boolean useVisualPadding = lc.isVisualPadding();
+
+               final ComponentWrapper[] comps = container.getComponents();
+
+               boolean hasTagged = false;  // So we do not have to sort if it will not do any good
+               boolean hasPushX = false, hasPushY = false;
+               boolean hitEndOfRow = false;
+               final int[] cellXY = new int[2];
+               final ArrayList<int[]> spannedRects = new ArrayList<int[]>(2);
+
+               final DimConstraint[] specs = (lc.isFlowX() ? rowConstr : colConstr).getConstaints();
+
+               int sizeGroupsX = 0, sizeGroupsY = 0;
+               int[] dockInsets = null;    // top, left, bottom, right insets for docks.
+
+               LinkHandler.clearTemporaryBounds(container.getLayout());
+
+               for (int i = 0; i < comps.length;) {
+                       ComponentWrapper comp = comps[i];
+                       CC rootCc = getCC(comp, ccMap);
+
+                       addLinkIDs(rootCc);
+
+                       int hideMode = comp.isVisible() ? -1 : rootCc.getHideMode() != -1 ? rootCc.getHideMode() : lc.getHideMode();
+
+                       if (hideMode == 3) { // To work with situations where there are components that does not have a layout manager, or not this one.
+                               setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
+                               i++;
+                               continue;   // The "external" component should not be handled further.
+                       }
+
+                       if (rootCc.getHorizontal().getSizeGroup() != null)
+                               sizeGroupsX++;
+                       if (rootCc.getVertical().getSizeGroup() != null)
+                               sizeGroupsY++;
+
+                       // Special treatment of absolute positioned components.
+                       if (getPos(comp, rootCc) != null || rootCc.isExternal()) {
+
+                               CompWrap cw = new CompWrap(comp, rootCc, hideMode, useVisualPadding);
+                               Cell cell = grid.get(null);
+                               if (cell == null) {
+                                       grid.put(null, new Cell(cw));
+                               } else {
+                                       cell.compWraps.add(cw);
+                               }
+
+                               if (!rootCc.isBoundsInGrid() || rootCc.isExternal()) {
+                                       setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
+                                       i++;
+                                       continue;
+                               }
+                       }
+
+                       if (rootCc.getDockSide() != -1) {
+                               if (dockInsets == null)
+                                       dockInsets = new int[] {-MAX_DOCK_GRID, -MAX_DOCK_GRID, MAX_DOCK_GRID, MAX_DOCK_GRID};
+
+                               addDockingCell(dockInsets, rootCc.getDockSide(), new CompWrap(comp, rootCc, hideMode, useVisualPadding));
+                               i++;
+                               continue;
+                       }
+
+                       Boolean cellFlowX = rootCc.getFlowX();
+                       Cell cell = null;
+
+                       if (rootCc.isNewline()) {
+                               wrap(cellXY, rootCc.getNewlineGapSize());
+                       } else if (hitEndOfRow) {
+                               wrap(cellXY, null);
+                       }
+                       hitEndOfRow = false;
+
+                       final boolean isRowInGridMode = !lc.isNoGrid() && !((DimConstraint) LayoutUtil.getIndexSafe(specs, lc.isFlowX() ? cellXY[1] : cellXY[0])).isNoGrid();
+
+                       // Move to a free y, x  if no absolute grid specified
+                       int cx = rootCc.getCellX();
+                       int cy = rootCc.getCellY();
+                       if ((cx < 0 || cy < 0) && isRowInGridMode && rootCc.getSkip() == 0) { // 3.7.2: If skip, don't find an empty cell first.
+                               while (!isCellFree(cellXY[1], cellXY[0], spannedRects)) {
+                                       if (Math.abs(increase(cellXY, 1)) >= wrap)
+                                               wrap(cellXY, null);
+                               }
+                       } else {
+                               if (cx >= 0 && cy >= 0) {
+                                       if (cy >= 0) {
+                                               cellXY[0] = cx;
+                                               cellXY[1] = cy;
+                                       } else {    // Only one coordinate is specified. Use the current row (flowx) or column (flowy) to fill in.
+                                               if (lc.isFlowX()) {
+                                                       cellXY[0] = cx;
+                                               } else {
+                                                       cellXY[1] = cx;
+                                               }
+                                       }
+                                       ensureIndexSizes(cx, cy);
+                               }
+                               cell = getCell(cellXY[1], cellXY[0]);   // Might be null
+                       }
+
+                       // Skip a number of cells. Changed for 3.6.1 to take wrap into account and thus "skip" to the next and possibly more rows.
+                       for (int s = 0, skipCount = rootCc.getSkip(); s < skipCount; s++) {
+                               do {
+                                       if (Math.abs(increase(cellXY, 1)) >= wrap)
+                                               wrap(cellXY, null);
+                               } while (!isCellFree(cellXY[1], cellXY[0], spannedRects));
+                       }
+
+                       // If cell is not created yet, create it and set it.
+                       if (cell == null) {
+                               int spanx = Math.min(!isRowInGridMode && lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanX(), MAX_GRID - cellXY[0]);
+                               int spany = Math.min(!isRowInGridMode && !lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanY(), MAX_GRID - cellXY[1]);
+
+                               cell = new Cell(spanx, spany, cellFlowX != null ? cellFlowX : lc.isFlowX());
+
+                               setCell(cellXY[1], cellXY[0], cell);
+
+                               // Add a rectangle so we can know that spanned cells occupy more space.
+                               if (spanx > 1 || spany > 1)
+                                       spannedRects.add(new int[] {cellXY[0], cellXY[1], spanx, spany});
+                       }
+
+                       // Add the one, or all, components that split the grid position to the same Cell.
+                       boolean wrapHandled = false;
+                       int splitLeft = isRowInGridMode ? rootCc.getSplit() - 1 : LayoutUtil.INF;
+                       boolean splitExit = false;
+                       final boolean spanRestOfRow = (lc.isFlowX() ? rootCc.getSpanX() : rootCc.getSpanY()) == LayoutUtil.INF;
+
+                       for (; splitLeft >= 0 && i < comps.length; splitLeft--) {
+                               ComponentWrapper compAdd = comps[i];
+                               CC cc = getCC(compAdd, ccMap);
+
+                               addLinkIDs(cc);
+
+                               boolean visible = compAdd.isVisible();
+                               hideMode = visible ? -1 : cc.getHideMode() != -1 ? cc.getHideMode() : lc.getHideMode();
+
+                               if (cc.isExternal() || hideMode == 3) {
+                                       i++;
+                                       splitLeft++;    // Added for 3.5.5 so that these components does not "take" a split slot.
+                                       continue;       // To work with situations where there are components that does not have a layout manager, or not this one.
+                               }
+
+                               hasPushX |= (visible || hideMode > 1) && (cc.getPushX() != null);
+                               hasPushY |= (visible || hideMode > 1) && (cc.getPushY() != null);
+
+                               if (cc != rootCc) { // If not first in a cell
+                                       if (cc.isNewline() || !cc.isBoundsInGrid() || cc.getDockSide() != -1)
+                                               break;
+
+                                       if (splitLeft > 0 && cc.getSkip() > 0) {
+                                               splitExit = true;
+                                               break;
+                                       }
+                               }
+
+                               CompWrap cw = new CompWrap(compAdd, cc, hideMode, useVisualPadding);
+                               cell.compWraps.add(cw);
+                               cell.hasTagged |= cc.getTag() != null;
+                               hasTagged |= cell.hasTagged;
+
+                               if (cc != rootCc) {
+                                       if (cc.getHorizontal().getSizeGroup() != null)
+                                               sizeGroupsX++;
+                                       if (cc.getVertical().getSizeGroup() != null)
+                                               sizeGroupsY++;
+                               }
+
+                               i++;
+
+                               if ((cc.isWrap() || (spanRestOfRow && splitLeft == 0))) {
+                                       if (cc.isWrap()) {
+                                               wrap(cellXY, cc.getWrapGapSize());
+                                       } else {
+                                               hitEndOfRow = true;
+                                       }
+                                       wrapHandled = true;
+                                       break;
+                               }
+                       }
+
+                       if (!wrapHandled && isRowInGridMode) {
+                               int span = lc.isFlowX() ? cell.spanx : cell.spany;
+                               if (Math.abs((lc.isFlowX() ? cellXY[0] : cellXY[1])) + span >= wrap) {
+                                       hitEndOfRow = true;
+                               } else {
+                                       increase(cellXY, splitExit ? span - 1 : span);
+                               }
+                       }
+               }
+
+               // If there were size groups, calculate the largest values in the groups (for min/pref/max) and enforce them on the rest in the group.
+               if (sizeGroupsX > 0 || sizeGroupsY > 0) {
+                       HashMap<String, int[]> sizeGroupMapX = sizeGroupsX > 0 ? new HashMap<String, int[]>(sizeGroupsX) : null;
+                       HashMap<String, int[]> sizeGroupMapY = sizeGroupsY > 0 ? new HashMap<String, int[]>(sizeGroupsY) : null;
+                       ArrayList<CompWrap> sizeGroupCWs = new ArrayList<CompWrap>(Math.max(sizeGroupsX, sizeGroupsY));
+
+                       for (Cell cell : grid.values()) {
+                               for (int i = 0; i < cell.compWraps.size(); i++) {
+                                       CompWrap cw = cell.compWraps.get(i);
+                                       String sgx = cw.cc.getHorizontal().getSizeGroup();
+                                       String sgy = cw.cc.getVertical().getSizeGroup();
+
+                                       if (sgx != null || sgy != null) {
+                                               if (sgx != null && sizeGroupMapX != null)
+                                                       addToSizeGroup(sizeGroupMapX, sgx, cw.getSizes(true));
+                                               if (sgy != null && sizeGroupMapY != null)
+                                                       addToSizeGroup(sizeGroupMapY, sgy, cw.getSizes(false));
+                                               sizeGroupCWs.add(cw);
+                                       }
+                               }
+                       }
+
+                       // Set/equalize the sizeGroups to same the values.
+                       for (CompWrap cw : sizeGroupCWs) {
+                               if (sizeGroupMapX != null)
+                                       cw.setForcedSizes(sizeGroupMapX.get(cw.cc.getHorizontal().getSizeGroup()), true);  // Target method handles null sizes
+                               if (sizeGroupMapY != null)
+                                       cw.setForcedSizes(sizeGroupMapY.get(cw.cc.getVertical().getSizeGroup()), false); // Target method handles null sizes
+                       }
+               } // Component loop
+
+               if (hasTagged)
+                       sortCellsByPlatform(grid.values(), container);
+
+               // Calculate gaps now that the cells are filled and we know all adjacent components.
+               boolean ltr = LayoutUtil.isLeftToRight(lc, container);
+               for (Cell cell : grid.values()) {
+                       ArrayList<CompWrap> cws = cell.compWraps;
+
+                       for (int i = 0, lastI = cws.size() - 1; i <= lastI; i++) {
+                               CompWrap cw = cws.get(i);
+                               ComponentWrapper cwBef = i > 0 ? cws.get(i - 1).comp : null;
+                               ComponentWrapper cwAft = i < lastI ? cws.get(i + 1).comp : null;
+
+                               String tag = getCC(cw.comp, ccMap).getTag();
+                               CC ccBef = cwBef != null ? getCC(cwBef, ccMap) : null;
+                               CC ccAft = cwAft != null ? getCC(cwAft, ccMap) : null;
+
+                               cw.calcGaps(cwBef, ccBef, cwAft, ccAft, tag, cell.flowx, ltr);
+                       }
+               }
+
+               dockOffX = getDockInsets(colIndexes);
+               dockOffY = getDockInsets(rowIndexes);
+
+               // Add synthetic indexes for empty rows and columns so they can get a size
+               ensureIndexSizes(colConstr.getCount(), rowConstr.getCount());
+
+               colGroupLists = divideIntoLinkedGroups(false);
+               rowGroupLists = divideIntoLinkedGroups(true);
+
+               pushXs = hasPushX || lc.isFillX() ? getDefaultPushWeights(false) : null;
+               pushYs = hasPushY || lc.isFillY() ? getDefaultPushWeights(true) : null;
+
+               if (LayoutUtil.isDesignTime(container))
+                       saveGrid(container, grid);
+       }
+
+       private void ensureIndexSizes(int colCount, int rowCount)
+       {
+               for (int i = 0; i < colCount; i++)
+                       colIndexes.add(i);
+               for (int i = 0; i < rowCount; i++)
+                       rowIndexes.add(i);
+       }
+
+       private static CC getCC(ComponentWrapper comp, Map<? extends ComponentWrapper, CC> ccMap)
+       {
+               CC cc = ccMap.get(comp);
+               return cc != null ? cc : DEF_CC;
+       }
+
+       private void addLinkIDs(CC cc)
+       {
+               String[] linkIDs = cc.getLinkTargets();
+               for (String linkID : linkIDs) {
+                       if (linkTargetIDs == null)
+                               linkTargetIDs = new HashMap<String, Boolean>();
+                       linkTargetIDs.put(linkID, null);
+               }
+       }
+
+       /** If the container (parent) that this grid is laying out has changed its bounds, call this method to
+        * clear any cached values min/pref/max sizes of the components and rows/columns.
+        * <p>
+        * If any component can have changed cell the grid needs to be recreated.
+        */
+       public void invalidateContainerSize()
+       {
+               colFlowSpecs = null;
+               invalidateComponentSizes();
+       }
+
+       private void invalidateComponentSizes()
+       {
+               for (Cell cell : grid.values()) {
+                       for (CompWrap compWrap : cell.compWraps)
+                               compWrap.invalidateSizes();
+               }
+       }
+
+       /**
+        * @deprecated since 5.0 Last boolean is not needed and is gotten from the new {@link net.miginfocom.layout.ComponentWrapper#getContentBias()} instead;
+        */
+       public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean notUsed)
+       {
+               return layoutImpl(bounds, alignX, alignY, debug, false);
+       }
+
+       /** Does the actual layout. Uses many values calculated in the constructor.
+        * @param bounds The bounds to layout against. Normally that of the parent. [x, y, width, height].
+        * @param alignX The alignment for the x-axis. Can be null.
+        * @param alignY The alignment for the y-axis. Can be null.
+        * @param debug If debug information should be saved in {@link #debugRects}.
+        * @return If the layout has changed the preferred size and there is need for a new layout. This can happen if one or more components
+        * in the grid has a content bias according to {@link net.miginfocom.layout.ComponentWrapper#getContentBias()}.
+        * @since 5.0
+        */
+       public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug)
+       {
+               return layoutImpl(bounds, alignX, alignY, debug, false);
+       }
+
+       /** Does the actual layout. Uses many values calculated in the constructor.
+        * @param bounds The bounds to layout against. Normally that of the parent. [x, y, width, height].
+        * @param alignX The alignment for the x-axis. Can be null.
+        * @param alignY The alignment for the y-axis. Can be null.
+        * @param debug If debug information should be saved in {@link #debugRects}.
+        * @param trialRun If true the bounds calculated will not be transferred to the components. Only the internal size
+        * of the components will be calculated.
+        * @return If the layout has changed the preferred size and there is need for a new layout. This can happen if one or more components
+        * in the grid has a content bias according to {@link net.miginfocom.layout.ComponentWrapper#getContentBias()}.
+        * @since 5.0
+        */
+       private boolean layoutImpl(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean trialRun)
+       {
+               if (debug)
+                       debugRects = new ArrayList<int[]>();
+
+               if (colFlowSpecs == null)
+                       checkSizeCalcs(bounds[2], bounds[3]);
+
+               resetLinkValues(true, true);
+
+               layoutInOneDim(bounds[2], alignX, false, pushXs);
+               layoutInOneDim(bounds[3], alignY, true, pushYs);
+
+               HashMap<String, Integer> endGrpXMap = null, endGrpYMap = null;
+               int compCount = container.getComponentCount();
+
+               // Transfer the calculated bound from the ComponentWrappers to the actual Components.
+               boolean addVisualPadding = lc.isVisualPadding();
+               boolean layoutAgain = false;
+               if (compCount > 0) {
+                       for (int j = 0; j < (linkTargetIDs != null ? 2 : 1); j++) {   // First do the calculations (maybe more than once) then set the bounds when done
+                               boolean doAgain;
+                               int count = 0;
+                               do {
+                                       doAgain = false;
+                                       for (Cell cell : grid.values()) {
+                                               for (CompWrap cw : cell.compWraps) {
+                                                       if (j == 0) {
+                                                               doAgain |= doAbsoluteCorrections(cw, bounds);
+                                                               if (!doAgain) { // If we are going to do this again, do not bother this time around
+                                                                       if (cw.cc.getHorizontal().getEndGroup() != null)
+                                                                               endGrpXMap = addToEndGroup(endGrpXMap, cw.cc.getHorizontal().getEndGroup(), cw.x + cw.w);
+
+                                                                       if (cw.cc.getVertical().getEndGroup() != null)
+                                                                               endGrpYMap = addToEndGroup(endGrpYMap, cw.cc.getVertical().getEndGroup(), cw.y + cw.h);
+                                                               }
+
+                                                               // @since 3.7.2 Needed or absolute "pos" pointing to "visual" or "container" didn't work if
+                                                               // their bounds changed during the layout cycle. At least not in SWT.
+                                                               if (linkTargetIDs != null && (linkTargetIDs.containsKey("visual") || linkTargetIDs.containsKey("container"))) {
+                                                                       layoutAgain = true;
+                                                               }
+                                                       }
+
+                                                       if (linkTargetIDs == null || j == 1) {
+                                                               if (cw.cc.getHorizontal().getEndGroup() != null)
+                                                                       cw.w = endGrpXMap.get(cw.cc.getHorizontal().getEndGroup()) - cw.x;
+
+                                                               if (cw.cc.getVertical().getEndGroup() != null)
+                                                                       cw.h = endGrpYMap.get(cw.cc.getVertical().getEndGroup()) - cw.y;
+
+                                                               cw.x += bounds[0];
+                                                               cw.y += bounds[1];
+
+                                                               if (!trialRun)
+                                                                       cw.transferBounds(addVisualPadding);
+
+                                                               if (callbackList != null) {
+                                                                       for (LayoutCallback callback : callbackList)
+                                                                               callback.correctBounds(cw.comp);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       clearGroupLinkBounds();
+                                       if (++count > ((compCount << 3) + 10)) {
+                                               System.err.println("Unstable cyclic dependency in absolute linked values.");
+                                               break;
+                                       }
+
+                               } while (doAgain);
+                       }
+               }
+
+               // Add debug shapes for the "cells". Use the CompWraps as base for inding the cells.
+               if (debug) {
+                       for (Cell cell : grid.values()) {
+                               ArrayList<CompWrap> compWraps = cell.compWraps;
+                               for (CompWrap cw : compWraps) {
+                                       LinkedDimGroup hGrp = getGroupContaining(colGroupLists, cw);
+                                       LinkedDimGroup vGrp = getGroupContaining(rowGroupLists, cw);
+
+                                       if (hGrp != null && vGrp != null)
+                                               debugRects.add(new int[]{hGrp.lStart + bounds[0] - (hGrp.fromEnd ? hGrp.lSize : 0), vGrp.lStart + bounds[1] - (vGrp.fromEnd ? vGrp.lSize : 0), hGrp.lSize, vGrp.lSize});
+                               }
+                       }
+               }
+               return layoutAgain;
+       }
+
+       public void paintDebug()
+       {
+               if (debugRects != null) {
+                       container.paintDebugOutline(lc.isVisualPadding());
+
+                       ArrayList<int[]> painted = new ArrayList<int[]>();
+                       for (int[] r : debugRects) {
+                               if (!painted.contains(r)) {
+                                       container.paintDebugCell(r[0], r[1], r[2], r[3]);
+                                       painted.add(r);
+                               }
+                       }
+
+                       for (Cell cell : grid.values()) {
+                               ArrayList<CompWrap> compWraps = cell.compWraps;
+                               for (CompWrap compWrap : compWraps)
+                                       compWrap.comp.paintDebugOutline(lc.isVisualPadding());
+                       }
+               }
+       }
+
+       public ContainerWrapper getContainer()
+       {
+               return container;
+       }
+
+       public final int[] getWidth()
+       {
+               return getWidth(lastRefHeight);
+       }
+
+       public final int[] getWidth(int refHeight)
+       {
+               checkSizeCalcs(lastRefWidth, refHeight);
+               return width.clone();
+       }
+
+       public final int[] getHeight()
+       {
+               return getHeight(lastRefWidth);
+       }
+
+       public final int[] getHeight(int refWidth)
+       {
+               checkSizeCalcs(refWidth, lastRefHeight);
+               return height.clone();
+       }
+
+       private int lastRefWidth = 0, lastRefHeight = 0;
+
+       private void checkSizeCalcs(int refWidth, int refHeight)
+       {
+               if (colFlowSpecs == null)
+                       calcGridSizes(refWidth, refHeight);
+
+               if ((refWidth > 0 && refWidth != lastRefWidth) || (refHeight > 0 && refHeight != lastRefHeight)) {
+                       int[] refBounds = new int[] {0, 0, (refWidth > 0 ? refWidth : width[LayoutUtil.PREF]), (refHeight > 0 ? refHeight : height[LayoutUtil.PREF])};
+                       layoutImpl(refBounds, null, null, false, true);
+                       calcGridSizes(refWidth, refHeight);
+               }
+
+               lastRefWidth = refWidth;
+               lastRefHeight = refHeight;
+       }
+
+       private void calcGridSizes(int refWidth, int refHeight)
+       {
+               // Note, in these calls the grid can be invalidated and specs set to null. Therefore use local versions.
+               FlowSizeSpec colSpecs = calcRowsOrColsSizes(true, refWidth);
+               FlowSizeSpec rowSpecs = calcRowsOrColsSizes(false, refHeight);
+
+               colFlowSpecs = colSpecs;
+               rowFlowSpecs = rowSpecs;
+
+               width = getMinPrefMaxSumSize(true, colSpecs.sizes);
+               height = getMinPrefMaxSumSize(false, rowSpecs.sizes);
+
+               if (linkTargetIDs == null) {
+                       resetLinkValues(false, true);
+               } else {
+                       // This call makes some components flicker on SWT. They get their bounds changed twice since
+                       // the change might affect the absolute size adjustment below. There's no way around this that
+                       // I know of.
+                       layout(new int[]{0, 0, refWidth, refHeight}, null, null, false);
+                       resetLinkValues(false, false);
+               }
+
+               adjustSizeForAbsolute(true);
+               adjustSizeForAbsolute(false);
+       }
+
+       private UnitValue[] getPos(ComponentWrapper cw, CC cc)
+       {
+               UnitValue[] callbackPos = null;
+               if (callbackList != null) {
+                       for (int i = 0; i < callbackList.size() && callbackPos == null; i++)
+                               callbackPos = callbackList.get(i).getPosition(cw);   // NOT a copy!
+               }
+
+               // If one is null, return the other (which many also be null)
+               UnitValue[] ccPos = cc.getPos();    // A copy!!
+               if (callbackPos == null || ccPos == null)
+                       return callbackPos != null ? callbackPos : ccPos;
+
+               // Merge
+               for (int i = 0; i < 4; i++) {
+                       UnitValue cbUv = callbackPos[i];
+                       if (cbUv != null)
+                               ccPos[i] = cbUv;
+               }
+
+               return ccPos;
+       }
+
+       private BoundSize[] getCallbackSize(ComponentWrapper cw)
+       {
+               if (callbackList != null) {
+                       for (LayoutCallback callback : callbackList) {
+                               BoundSize[] bs = callback.getSize(cw);   // NOT a copy!
+                               if (bs != null)
+                                       return bs;
+                       }
+               }
+               return null;
+       }
+
+       private static int getDockInsets(TreeSet<Integer> set)
+       {
+               int c = 0;
+               for (Integer i : set) {
+                       if (i < -MAX_GRID) {
+                               c++;
+                       } else {
+                               break;  // Since they are sorted we can break
+                       }
+               }
+               return c;
+       }
+
+       /**
+        * @param cw Never <code>null</code>.
+        * @param cc Never <code>null</code>.
+        * @param external The bounds should be stored even if they are not in {@link #linkTargetIDs}.
+        * @return If a change has been made.
+        */
+       private boolean setLinkedBounds(ComponentWrapper cw, CC cc, int x, int y, int w, int h, boolean external)
+       {
+               String id = cc.getId() != null ? cc.getId() : cw.getLinkId();
+               if (id == null)
+                       return false;
+
+               String gid = null;
+               int grIx = id.indexOf('.');
+               if (grIx != -1 ) {
+                       gid = id.substring(0, grIx);
+                       id = id.substring(grIx + 1);
+               }
+
+               Object lay = container.getLayout();
+               boolean changed = false;
+               if (external || (linkTargetIDs != null && linkTargetIDs.containsKey(id)))
+                       changed = LinkHandler.setBounds(lay, id, x, y, w, h, !external, false);
+
+               if (gid != null && (external || (linkTargetIDs != null && linkTargetIDs.containsKey(gid)))) {
+                       if (linkTargetIDs == null)
+                               linkTargetIDs = new HashMap<String, Boolean>(4);
+
+                       linkTargetIDs.put(gid, Boolean.TRUE);
+                       changed |= LinkHandler.setBounds(lay, gid, x, y, w, h, !external, true);
+               }
+
+               return changed;
+       }
+
+       /** Go to next cell.
+        * @param p The point to increase
+        * @param cnt How many cells to advance.
+        * @return The new value in the "increasing" dimension.
+        */
+       private int increase(int[] p, int cnt)
+       {
+               return lc.isFlowX() ? (p[0] += cnt) : (p[1] += cnt);
+       }
+
+       /** Wraps to the next row or column depending on if horizontal flow or vertical flow is used.
+        * @param cellXY The point to wrap and thus set either x or y to 0 and increase the other one.
+        * @param gapSize The gaps size specified in a "wrap XXX" or "newline XXX" or <code>null</code> if none.
+        */
+       private void wrap(int[] cellXY, BoundSize gapSize)
+       {
+               boolean flowx = lc.isFlowX();
+               cellXY[0] = flowx ? 0 : cellXY[0] + 1;
+               cellXY[1] = flowx ? cellXY[1] + 1 : 0;
+
+               if (gapSize != null) {
+                       if (wrapGapMap == null)
+                               wrapGapMap = new HashMap<Integer, BoundSize>(8);
+
+                       wrapGapMap.put(cellXY[flowx ? 1 : 0], gapSize);
+               }
+
+               // add the row/column so that the gap in the last row/col will not be removed.
+               if (flowx) {
+                       rowIndexes.add(cellXY[1]);
+               } else {
+                       colIndexes.add(cellXY[0]);
+               }
+       }
+
+       /** Sort components (normally buttons in a button bar) so they appear in the correct order.
+        * @param cells The cells to sort.
+        * @param parent The parent.
+        */
+       private static void sortCellsByPlatform(Collection<Cell> cells, ContainerWrapper parent)
+       {
+               String order = PlatformDefaults.getButtonOrder();
+               String orderLo = order.toLowerCase();
+
+               int unrelSize = PlatformDefaults.convertToPixels(1, "u", true, 0, parent, null);
+
+               if (unrelSize == UnitConverter.UNABLE)
+                       throw new IllegalArgumentException("'unrelated' not recognized by PlatformDefaults!");
+
+               int[] gapUnrel = new int[] {unrelSize, unrelSize, LayoutUtil.NOT_SET};
+               int[] flGap = new int[] {0, 0, LayoutUtil.NOT_SET};
+
+               for (Cell cell : cells) {
+                       if (!cell.hasTagged)
+                               continue;
+
+                       CompWrap prevCW = null;
+                       boolean nextUnrel = false;
+                       boolean nextPush = false;
+                       ArrayList<CompWrap> sortedList = new ArrayList<CompWrap>(cell.compWraps.size());
+
+                       for (int i = 0, iSz = orderLo.length(); i < iSz; i++) {
+                               char c = orderLo.charAt(i);
+                               if (c == '+' || c == '_') {
+                                       nextUnrel = true;
+                                       if (c == '+')
+                                               nextPush = true;
+                               } else {
+                                       String tag = PlatformDefaults.getTagForChar(c);
+                                       if (tag != null) {
+                                               for (int j = 0, jSz = cell.compWraps.size(); j < jSz; j++) {
+                                                       CompWrap cw = cell.compWraps.get(j);
+                                                       if (tag.equals(cw.cc.getTag())) {
+                                                               if (Character.isUpperCase(order.charAt(i)))
+                                                                       cw.adjustMinHorSizeUp((int) PlatformDefaults.getMinimumButtonWidthIncludingPadding(0, parent, cw.comp));
+
+                                                               sortedList.add(cw);
+
+                                                               if (nextUnrel) {
+                                                                       (prevCW != null ? prevCW : cw).mergeGapSizes(gapUnrel, cell.flowx, prevCW == null);
+                                                                       if (nextPush) {
+                                                                               cw.forcedPushGaps = 1;
+                                                                               nextUnrel = false;
+                                                                               nextPush = false;
+                                                                       }
+                                                               }
+
+                                                               // "unknown" components will always get an Unrelated gap.
+                                                               if (c == 'u')
+                                                                       nextUnrel = true;
+                                                               prevCW = cw;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       // If we have a gap that was supposed to push but no more components was found to but the "gap before" then compensate.
+                       if (sortedList.size() > 0) {
+                               CompWrap cw = sortedList.get(sortedList.size() - 1);
+                               if (nextUnrel) {
+                                       cw.mergeGapSizes(gapUnrel, cell.flowx, false);
+                                       if (nextPush)
+                                               cw.forcedPushGaps |= 2;
+                               }
+
+                               // Remove first and last gap if not set explicitly.
+                               if (cw.cc.getHorizontal().getGapAfter() == null)
+                                       cw.setGaps(flGap, 3);
+
+                               cw = sortedList.get(0);
+                               if (cw.cc.getHorizontal().getGapBefore() == null)
+                                       cw.setGaps(flGap, 1);
+                       }
+
+                       // Exchange the unsorted CompWraps for the sorted one.
+                       if (cell.compWraps.size() == sortedList.size()) {
+                               cell.compWraps.clear();
+                       } else {
+                               cell.compWraps.removeAll(sortedList);
+                       }
+                       cell.compWraps.addAll(sortedList);
+               }
+       }
+
+       private Float[] getDefaultPushWeights(boolean isRows)
+       {
+               ArrayList<LinkedDimGroup>[] groupLists = isRows ? rowGroupLists : colGroupLists;
+
+               Float[] pushWeightArr = GROW_100;   // Only create specific if any of the components have grow.
+               for (int i = 0, ix = 1; i < groupLists.length; i++, ix += 2) {
+                       ArrayList<LinkedDimGroup> grps = groupLists[i];
+                       Float rowPushWeight = null;
+                       for (LinkedDimGroup grp : grps) {
+                               for (int c = 0; c < grp._compWraps.size(); c++) {
+                                       CompWrap cw = grp._compWraps.get(c);
+                                       int hideMode = cw.comp.isVisible() ? -1 : cw.cc.getHideMode() != -1 ? cw.cc.getHideMode() : lc.getHideMode();
+
+                                       Float pushWeight = hideMode < 2 ? (isRows ? cw.cc.getPushY() : cw.cc.getPushX()) : null;
+                                       if (rowPushWeight == null || (pushWeight != null && pushWeight > rowPushWeight))
+                                               rowPushWeight = pushWeight;
+                               }
+                       }
+
+                       if (rowPushWeight != null) {
+                               if (pushWeightArr == GROW_100)
+                                       pushWeightArr = new Float[(groupLists.length << 1) + 1];
+                               pushWeightArr[ix] = rowPushWeight;
+                       }
+               }
+
+               return pushWeightArr;
+       }
+
+       private void clearGroupLinkBounds()
+       {
+               if (linkTargetIDs == null)
+                       return;
+
+               for (Map.Entry<String, Boolean> o : linkTargetIDs.entrySet()) {
+                       if (o.getValue() == Boolean.TRUE)
+                               LinkHandler.clearBounds(container.getLayout(), o.getKey());
+               }
+       }
+
+       private void resetLinkValues(boolean parentSize, boolean compLinks)
+       {
+               Object lay = container.getLayout();
+               if (compLinks)
+                       LinkHandler.clearTemporaryBounds(lay);
+
+               boolean defIns = !hasDocks();
+
+               int parW = parentSize ? lc.getWidth().constrain(container.getWidth(), getParentSize(container, true), container) : 0;
+               int parH = parentSize ? lc.getHeight().constrain(container.getHeight(), getParentSize(container, false), container) : 0;
+
+               int insX = LayoutUtil.getInsets(lc, 0, defIns).getPixels(0, container, null);
+               int insY = LayoutUtil.getInsets(lc, 1, defIns).getPixels(0, container, null);
+               int visW = parW - insX - LayoutUtil.getInsets(lc, 2, defIns).getPixels(0, container, null);
+               int visH = parH - insY - LayoutUtil.getInsets(lc, 3, defIns).getPixels(0, container, null);
+
+               LinkHandler.setBounds(lay, "visual", insX, insY, visW, visH, true, false);
+               LinkHandler.setBounds(lay, "container", 0, 0, parW, parH, true, false);
+       }
+
+       /** Returns the {@link net.miginfocom.layout.Grid.LinkedDimGroup} that has the {@link net.miginfocom.layout.Grid.CompWrap}
+        * <code>cw</code>.
+        * @param groupLists The lists to search in.
+        * @param cw The component wrap to find.
+        * @return The linked group or <code>null</code> if none had the component wrap.
+        */
+       private static LinkedDimGroup getGroupContaining(ArrayList<LinkedDimGroup>[] groupLists, CompWrap cw)
+       {
+               for (ArrayList<LinkedDimGroup> groups : groupLists) {
+            for (LinkedDimGroup group : groups) {
+                ArrayList<CompWrap> cwList = group._compWraps;
+                for (CompWrap aCwList : cwList) {
+                    if (aCwList == cw)
+                        return group;
+                }
+            }
+               }
+               return null;
+       }
+
+       private boolean doAbsoluteCorrections(CompWrap cw, int[] bounds)
+       {
+               boolean changed = false;
+
+               int[] stSz = getAbsoluteDimBounds(cw, bounds[2], true);
+               if (stSz != null)
+                       cw.setDimBounds(stSz[0], stSz[1], true);
+
+               stSz = getAbsoluteDimBounds(cw, bounds[3], false);
+               if (stSz != null)
+                       cw.setDimBounds(stSz[0], stSz[1], false);
+
+               // If there is a link id, store the new bounds.
+               if (linkTargetIDs != null)
+                       changed = setLinkedBounds(cw.comp, cw.cc, cw.x, cw.y, cw.w, cw.h, false);
+
+               return changed;
+       }
+
+       /** Adjust grid's width or height for the absolute components' positions.
+        */
+       private void adjustSizeForAbsolute(boolean isHor)
+       {
+               int[] curSizes = isHor ? width : height;
+
+               Cell absCell = grid.get(null);
+               if (absCell == null || absCell.compWraps.size() == 0)
+                       return;
+
+               ArrayList<CompWrap> cws = absCell.compWraps;
+
+               int maxEnd = 0;
+               for (int j = 0, cwSz = absCell.compWraps.size(); j < cwSz + 3; j++) {  // "Do Again" max absCell.compWraps.size() + 3 times.
+                       boolean doAgain = false;
+                       for (int i = 0; i < cwSz; i++) {
+                               CompWrap cw = cws.get(i);
+                               int[] stSz = getAbsoluteDimBounds(cw, 0, isHor);
+                               int end = stSz[0] + stSz[1];
+                               if (maxEnd < end)
+                                       maxEnd = end;
+
+                               // If there is a link id, store the new bounds.
+                               if (linkTargetIDs != null)
+                                       doAgain |= setLinkedBounds(cw.comp, cw.cc, stSz[0], stSz[0], stSz[1], stSz[1], false);
+                       }
+                       if (!doAgain)
+                               break;
+
+                       // We need to check this again since the coords may be smaller this round.
+                       maxEnd = 0;
+                       clearGroupLinkBounds();
+               }
+
+               maxEnd += LayoutUtil.getInsets(lc, isHor ? 3 : 2, !hasDocks()).getPixels(0, container, null);
+
+               if (curSizes[LayoutUtil.MIN] < maxEnd)
+                       curSizes[LayoutUtil.MIN] = maxEnd;
+               if (curSizes[LayoutUtil.PREF] < maxEnd)
+                       curSizes[LayoutUtil.PREF] = maxEnd;
+       }
+
+       private int[] getAbsoluteDimBounds(CompWrap cw, int refSize, boolean isHor)
+       {
+               if (cw.cc.isExternal()) {
+                       if (isHor) {
+                               return new int[] {cw.comp.getX(), cw.comp.getWidth()};
+                       } else {
+                               return new int[] {cw.comp.getY(), cw.comp.getHeight()};
+                       }
+               }
+
+               UnitValue[] pad = cw.cc.getPadding();
+
+               // If no changes do not create a lot of objects
+               UnitValue[] pos = getPos(cw.comp, cw.cc);
+               if (pos == null && pad == null)
+                       return null;
+
+               // Set start
+               int st = isHor ? cw.x : cw.y;
+               int sz = isHor ? cw.w : cw.h;
+
+               // If absolute, use those coordinates instead.
+               if (pos != null) {
+                       UnitValue stUV = pos[isHor ? 0 : 1];
+                       UnitValue endUV = pos[isHor ? 2 : 3];
+
+                       int minSz = cw.getSize(LayoutUtil.MIN, isHor);
+                       int maxSz = cw.getSize(LayoutUtil.MAX, isHor);
+                       sz = Math.min(Math.max(cw.getSize(LayoutUtil.PREF, isHor), minSz), maxSz);
+
+                       if (stUV != null) {
+                               st = stUV.getPixels(stUV.getUnit() == UnitValue.ALIGN ? sz : refSize, container, cw.comp);
+
+                               if (endUV != null)  // if (endUV == null && cw.cc.isBoundsIsGrid() == true)
+                                       sz = Math.min(Math.max((isHor ? (cw.x + cw.w) : (cw.y + cw.h)) - st, minSz), maxSz);
+                       }
+
+                       if (endUV != null) {
+                               if (stUV != null) {   // if (stUV != null || cw.cc.isBoundsIsGrid()) {
+                                       sz = Math.min(Math.max(endUV.getPixels(refSize, container, cw.comp) - st, minSz), maxSz);
+                               } else {
+                                       st = endUV.getPixels(refSize, container, cw.comp) - sz;
+                               }
+                       }
+               }
+
+               // If constraint has padding -> correct the start/size
+               if (pad != null) {
+                       UnitValue uv = pad[isHor ? 1 : 0];
+                       int p = uv != null ? uv.getPixels(refSize, container, cw.comp) : 0;
+                       st += p;
+                       uv = pad[isHor ? 3 : 2];
+                       sz += -p + (uv != null ? uv.getPixels(refSize, container, cw.comp) : 0);
+               }
+
+               return new int[] {st, sz};
+       }
+
+       private void layoutInOneDim(int refSize, UnitValue align, boolean isRows, Float[] defaultPushWeights)
+       {
+               boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
+               DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
+               FlowSizeSpec fss = isRows ? rowFlowSpecs : colFlowSpecs;
+               ArrayList<LinkedDimGroup>[] rowCols = isRows ? rowGroupLists : colGroupLists;
+
+               int[] rowColSizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, defaultPushWeights, LayoutUtil.PREF, refSize);
+
+               if (LayoutUtil.isDesignTime(container)) {
+                       TreeSet<Integer> indexes = isRows ? rowIndexes : colIndexes;
+                       int[] ixArr = new int[indexes.size()];
+                       int ix = 0;
+                       for (Integer i : indexes)
+                               ixArr[ix++] = i;
+
+                       putSizesAndIndexes(container.getComponent(), rowColSizes, ixArr, isRows);
+               }
+
+               int curPos = align != null ? align.getPixels(refSize - LayoutUtil.sum(rowColSizes), container, null) : 0;
+
+               if (fromEnd)
+                       curPos = refSize - curPos;
+
+               for (int i = 0 ; i < rowCols.length; i++) {
+                       ArrayList<LinkedDimGroup> linkedGroups = rowCols[i];
+                       int scIx = i - (isRows ? dockOffY : dockOffX);
+
+                       int bIx = i << 1;
+                       int bIx2 = bIx + 1;
+
+                       curPos += (fromEnd ? -rowColSizes[bIx] : rowColSizes[bIx]);
+
+                       DimConstraint primDC = scIx >= 0 ? primDCs[scIx >= primDCs.length ? primDCs.length - 1 : scIx] : DOCK_DIM_CONSTRAINT;
+
+                       int rowSize = rowColSizes[bIx2];
+
+                       for (LinkedDimGroup group : linkedGroups) {
+                               int groupSize = rowSize;
+                               if (group.span > 1)
+                                       groupSize = LayoutUtil.sum(rowColSizes, bIx2, Math.min((group.span << 1) - 1, rowColSizes.length - bIx2 - 1));
+
+                               group.layout(primDC, curPos, groupSize, group.span);
+                       }
+
+                       curPos += (fromEnd ? -rowSize : rowSize);
+               }
+       }
+
+       private static void addToSizeGroup(HashMap<String, int[]> sizeGroups, String sizeGroup, int[] size)
+       {
+               int[] sgSize = sizeGroups.get(sizeGroup);
+               if (sgSize == null) {
+                       sizeGroups.put(sizeGroup, new int[] {size[LayoutUtil.MIN], size[LayoutUtil.PREF], size[LayoutUtil.MAX]});
+               } else {
+                       sgSize[LayoutUtil.MIN] = Math.max(size[LayoutUtil.MIN], sgSize[LayoutUtil.MIN]);
+                       sgSize[LayoutUtil.PREF] = Math.max(size[LayoutUtil.PREF], sgSize[LayoutUtil.PREF]);
+                       sgSize[LayoutUtil.MAX] = Math.min(size[LayoutUtil.MAX], sgSize[LayoutUtil.MAX]);
+               }
+       }
+
+       private static HashMap<String, Integer> addToEndGroup(HashMap<String, Integer> endGroups, String endGroup, int end)
+       {
+               if (endGroup != null) {
+                       if (endGroups == null)
+                               endGroups = new HashMap<String, Integer>(4);
+
+                       Integer oldEnd = endGroups.get(endGroup);
+                       if (oldEnd == null || end > oldEnd)
+                               endGroups.put(endGroup, end);
+               }
+               return endGroups;
+       }
+
+       /** Calculates Min, Preferred and Max size for the columns OR rows.
+        * @param isHor If it is the horizontal dimension to calculate.
+        * @param containerSize The reference container size in the dimension. If <= 0 it will be replaced by the actual container's size.
+        * @return The sizes in a {@link net.miginfocom.layout.Grid.FlowSizeSpec}.
+        */
+       private FlowSizeSpec calcRowsOrColsSizes(boolean isHor, int containerSize)
+       {
+               ArrayList<LinkedDimGroup>[] groupsLists = isHor ? colGroupLists : rowGroupLists;
+               Float[] defPush = isHor ? pushXs : pushYs;
+
+               if (containerSize <= 0)
+                       containerSize = isHor ? container.getWidth() : container.getHeight();
+
+               BoundSize cSz = isHor ? lc.getWidth() : lc.getHeight();
+               if (!cSz.isUnset())
+                       containerSize = cSz.constrain(containerSize, getParentSize(container, isHor), container);
+
+               DimConstraint[] primDCs = (isHor? colConstr : rowConstr).getConstaints();
+               TreeSet<Integer> primIndexes = isHor ? colIndexes : rowIndexes;
+
+               int[][] rowColBoundSizes = new int[primIndexes.size()][];
+               HashMap<String, int[]> sizeGroupMap = new HashMap<String, int[]>(4);
+               DimConstraint[] allDCs = new DimConstraint[primIndexes.size()];
+
+               Iterator<Integer> primIt = primIndexes.iterator();
+               for (int r = 0; r < rowColBoundSizes.length; r++) {
+                       int cellIx = primIt.next();
+                       int[] rowColSizes = new int[3];
+
+                       if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID) {  // If not dock cell
+                               allDCs[r] = primDCs[cellIx >= primDCs.length ? primDCs.length - 1 : cellIx];
+                       } else {
+                               allDCs[r] = DOCK_DIM_CONSTRAINT;
+                       }
+
+                       ArrayList<LinkedDimGroup> groups = groupsLists[r];
+
+                       int[] groupSizes = new int[] {
+                                       getTotalGroupsSizeParallel(groups, LayoutUtil.MIN, false),
+                                       getTotalGroupsSizeParallel(groups, LayoutUtil.PREF, false),
+                                       LayoutUtil.INF};
+
+                       correctMinMax(groupSizes);
+                       BoundSize dimSize = allDCs[r].getSize();
+
+                       for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
+
+                               int rowColSize = groupSizes[sType];
+
+                               UnitValue uv = dimSize.getSize(sType);
+                               if (uv != null) {
+                                       // If the size of the column is a link to some other size, use that instead
+                                       int unit = uv.getUnit();
+                                       if (unit == UnitValue.PREF_SIZE) {
+                                               rowColSize = groupSizes[LayoutUtil.PREF];
+                                       } else if (unit == UnitValue.MIN_SIZE) {
+                                               rowColSize = groupSizes[LayoutUtil.MIN];
+                                       } else if (unit == UnitValue.MAX_SIZE) {
+                                               rowColSize = groupSizes[LayoutUtil.MAX];
+                                       } else {
+                                               rowColSize = uv.getPixels(containerSize, container, null);
+                                       }
+                               } else if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID && rowColSize == 0) {
+                                       rowColSize = LayoutUtil.isDesignTime(container) ? LayoutUtil.getDesignTimeEmptySize() : 0;    // Empty rows with no size set gets XX pixels if design time
+                               }
+
+                               rowColSizes[sType] = rowColSize;
+                       }
+
+                       correctMinMax(rowColSizes);
+                       addToSizeGroup(sizeGroupMap, allDCs[r].getSizeGroup(), rowColSizes);
+
+                       rowColBoundSizes[r] = rowColSizes;
+               }
+
+               // Set/equalize the size groups to same the values.
+               if (sizeGroupMap.size() > 0) {
+                       for (int r = 0; r < rowColBoundSizes.length; r++) {
+                               if (allDCs[r].getSizeGroup() != null)
+                                       rowColBoundSizes[r] = sizeGroupMap.get(allDCs[r].getSizeGroup());
+                       }
+               }
+
+               // Add the gaps
+               ResizeConstraint[] resConstrs = getRowResizeConstraints(allDCs);
+
+               boolean[] fillInPushGaps = new boolean[allDCs.length + 1];
+               int[][] gapSizes = getRowGaps(allDCs, containerSize, isHor, fillInPushGaps);
+
+               FlowSizeSpec fss = mergeSizesGapsAndResConstrs(resConstrs, fillInPushGaps, rowColBoundSizes, gapSizes);
+
+               // Spanning components are not handled yet. Check and adjust the multi-row min/pref they enforce.
+               adjustMinPrefForSpanningComps(allDCs, defPush, fss, groupsLists);
+
+               return fss;
+       }
+
+       private static int getParentSize(ComponentWrapper cw, boolean isHor)
+       {
+               ContainerWrapper p = cw.getParent();
+               return p != null ? (isHor ? cw.getWidth() : cw.getHeight()) : 0;
+       }
+
+       private int[] getMinPrefMaxSumSize(boolean isHor, int[][] sizes)
+       {
+               int[] retSizes = new int[3];
+
+               BoundSize sz = isHor ? lc.getWidth() : lc.getHeight();
+
+               for (int i = 0; i < sizes.length; i++) {
+                       if (sizes[i] != null) {
+                               int[] size = sizes[i];
+                               for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
+                                       if (sz.getSize(sType) != null) {
+                                               if (i == 0)
+                                                       retSizes[sType] = sz.getSize(sType).getPixels(getParentSize(container, isHor), container, null);
+                                       } else {
+                                               int s = size[sType];
+
+                                               if (s != LayoutUtil.NOT_SET) {
+                                                       if (sType == LayoutUtil.PREF) {
+                                                               int bnd = size[LayoutUtil.MAX];
+                                                               if (bnd != LayoutUtil.NOT_SET && bnd < s)
+                                                                       s = bnd;
+
+                                                               bnd = size[LayoutUtil.MIN];
+                                                               if (bnd > s)    // Includes s == LayoutUtil.NOT_SET since < 0.
+                                                                       s = bnd;
+                                                       }
+
+                                                       retSizes[sType] += s;   // MAX compensated below.
+                                               }
+
+                                               // So that MAX is always correct.
+                                               if (size[LayoutUtil.MAX] == LayoutUtil.NOT_SET || retSizes[LayoutUtil.MAX] > LayoutUtil.INF)
+                                                       retSizes[LayoutUtil.MAX] = LayoutUtil.INF;
+                                       }
+                               }
+                       }
+               }
+
+               correctMinMax(retSizes);
+
+               return retSizes;
+       }
+
+       private static ResizeConstraint[] getRowResizeConstraints(DimConstraint[] specs)
+       {
+               ResizeConstraint[] resConsts = new ResizeConstraint[specs.length];
+               for (int i = 0; i < resConsts.length; i++)
+                       resConsts[i] = specs[i].resize;
+               return resConsts;
+       }
+
+       private static ResizeConstraint[] getComponentResizeConstraints(ArrayList<CompWrap> compWraps, boolean isHor)
+       {
+               ResizeConstraint[] resConsts = new ResizeConstraint[compWraps.size()];
+               for (int i = 0; i < resConsts.length; i++) {
+                       CC fc = compWraps.get(i).cc;
+                       resConsts[i] = fc.getDimConstraint(isHor).resize;
+
+                       // Always grow docking components in the correct dimension.
+                       int dock = fc.getDockSide();
+                       if (isHor ? (dock == 0 || dock == 2) : (dock == 1 || dock == 3)) {
+                               ResizeConstraint dc = resConsts[i];
+                               resConsts[i] = new ResizeConstraint(dc.shrinkPrio, dc.shrink, dc.growPrio, ResizeConstraint.WEIGHT_100);
+                       }
+               }
+               return resConsts;
+       }
+
+       private static boolean[] getComponentGapPush(ArrayList<CompWrap> compWraps, boolean isHor)
+       {
+               // Make one element bigger and or the after gap with the next before gap.
+               boolean[] barr = new boolean[compWraps.size() + 1];
+               for (int i = 0; i < barr.length; i++) {
+
+                       boolean push = i > 0 && compWraps.get(i - 1).isPushGap(isHor, false);
+
+                       if (!push && i < (barr.length - 1))
+                               push = compWraps.get(i).isPushGap(isHor, true);
+
+                       barr[i] = push;
+               }
+               return barr;
+       }
+
+       /** Returns the row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
+        * @param specs
+        * @param refSize
+        * @param isHor
+        * @param fillInPushGaps If the gaps are pushing. <b>NOTE!</b> this argument will be filled in and thus changed!
+        * @return The row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
+        */
+       private int[][] getRowGaps(DimConstraint[] specs, int refSize, boolean isHor, boolean[] fillInPushGaps)
+       {
+               BoundSize defGap = isHor ? lc.getGridGapX() : lc.getGridGapY();
+               if (defGap == null)
+                       defGap = isHor ? PlatformDefaults.getGridGapX() : PlatformDefaults.getGridGapY();
+               int[] defGapArr = defGap.getPixelSizes(refSize, container, null);
+
+               boolean defIns = !hasDocks();
+
+               UnitValue firstGap = LayoutUtil.getInsets(lc, isHor ? 1 : 0, defIns);
+               UnitValue lastGap = LayoutUtil.getInsets(lc, isHor ? 3 : 2, defIns);
+
+        int[][] retValues = new int[specs.length + 1][];
+
+               for (int i = 0, wgIx = 0; i < retValues.length; i++) {
+               DimConstraint specBefore = i > 0 ? specs[i - 1] : null;
+                       DimConstraint specAfter = i < specs.length ? specs[i] : null;
+
+                       // No gap if between docking components.
+                       boolean edgeBefore = (specBefore == DOCK_DIM_CONSTRAINT || specBefore == null);
+                       boolean edgeAfter = (specAfter == DOCK_DIM_CONSTRAINT || specAfter == null);
+                       if (edgeBefore && edgeAfter)
+                               continue;
+
+                       BoundSize wrapGapSize = (wrapGapMap == null || isHor == lc.isFlowX() ? null : wrapGapMap.get(wgIx++));
+
+                       if (wrapGapSize == null) {
+
+                               int[] gapBefore = specBefore != null ? specBefore.getRowGaps(container, null, refSize, false) : null;
+                               int[] gapAfter = specAfter != null ? specAfter.getRowGaps(container, null, refSize, true) : null;
+
+                               if (edgeBefore && gapAfter == null && firstGap != null) {
+
+                                       int bef = firstGap.getPixels(refSize, container, null);
+                                       retValues[i] = new int[] {bef, bef, bef};
+
+                               } else if (edgeAfter && gapBefore == null && firstGap != null) {
+
+                                       int aft = lastGap.getPixels(refSize, container, null);
+                                       retValues[i] = new int[] {aft, aft, aft};
+
+                               } else {
+                                       retValues[i] = gapAfter != gapBefore ? mergeSizes(gapAfter, gapBefore) : new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
+                               }
+
+                               if (specBefore != null && specBefore.isGapAfterPush() || specAfter != null && specAfter.isGapBeforePush())
+                                       fillInPushGaps[i] = true;
+                       } else {
+
+                               if (wrapGapSize.isUnset()) {
+                                       retValues[i] = new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
+                               } else {
+                                       retValues[i] = wrapGapSize.getPixelSizes(refSize, container, null);
+                               }
+                               fillInPushGaps[i] = wrapGapSize.getGapPush();
+                       }
+        }
+        return retValues;
+    }
+
+       private static int[][] getGaps(ArrayList<CompWrap> compWraps, boolean isHor)
+       {
+               int compCount = compWraps.size();
+        int[][] retValues = new int[compCount + 1][];
+
+               retValues[0] = compWraps.get(0).getGaps(isHor, true);
+        for (int i = 0; i < compCount; i++) {
+               int[] gap1 = compWraps.get(i).getGaps(isHor, false);
+               int[] gap2 = i < compCount - 1 ? compWraps.get(i + 1).getGaps(isHor, true) : null;
+
+                       retValues[i + 1] = mergeSizes(gap1, gap2);
+        }
+
+        return retValues;
+    }
+
+       private boolean hasDocks()
+       {
+               return (dockOffX > 0 || dockOffY > 0 || rowIndexes.last() > MAX_GRID || colIndexes.last() > MAX_GRID);
+       }
+
+       /** Adjust min/pref size for columns(or rows) that has components that spans multiple columns (or rows).
+        * @param specs The specs for the columns or rows. Last index will be used if <code>count</code> is greater than this array's length.
+        * @param defPush The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+        * @param fss
+        * @param groupsLists
+        */
+       private void adjustMinPrefForSpanningComps(DimConstraint[] specs, Float[] defPush, FlowSizeSpec fss, ArrayList<LinkedDimGroup>[] groupsLists)
+       {
+               for (int r = groupsLists.length - 1; r >= 0; r--) { // Since 3.7.3 Iterate from end to start. Will solve some multiple spanning components hard to solve problems.
+                       ArrayList<LinkedDimGroup> groups = groupsLists[r];
+
+                       for (LinkedDimGroup group : groups) {
+                               if (group.span == 1)
+                                       continue;
+
+                               int[] sizes = group.getMinPrefMax();
+                               for (int s = LayoutUtil.MIN; s <= LayoutUtil.PREF; s++) {
+                                       int cSize = sizes[s];
+                                       if (cSize == LayoutUtil.NOT_SET)
+                                               continue;
+
+                                       int rowSize = 0;
+                                       int sIx = (r << 1) + 1;
+                                       int len = Math.min((group.span << 1), fss.sizes.length - sIx) - 1;
+                                       for (int j = sIx; j < sIx + len; j++) {
+                                               int sz = fss.sizes[j][s];
+                                               if (sz != LayoutUtil.NOT_SET)
+                                                       rowSize += sz;
+                                       }
+
+                                       if (rowSize < cSize && len > 0) {
+                                               for (int eagerness = 0, newRowSize = 0; eagerness < 4 && newRowSize < cSize; eagerness++)
+                                                       newRowSize = fss.expandSizes(specs, defPush, cSize, sIx, len, s, eagerness);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /** For one dimension divide the component wraps into logical groups. One group for component wraps that share a common something,
+        * line the property to layout by base line.
+        * @param isRows If rows, and not columns, are to be divided.
+        * @return One <code>ArrayList<LinkedDimGroup></code> for every row/column.
+        */
+       private ArrayList<LinkedDimGroup>[] divideIntoLinkedGroups(boolean isRows)
+       {
+               boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
+               TreeSet<Integer> primIndexes = isRows ? rowIndexes : colIndexes;
+               TreeSet<Integer> secIndexes = isRows ? colIndexes : rowIndexes;
+               DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
+
+               @SuppressWarnings("unchecked")
+               ArrayList<LinkedDimGroup>[] groupLists = new ArrayList[primIndexes.size()];
+
+               int gIx = 0;
+               for (int i : primIndexes) {
+
+                       DimConstraint dc;
+                       if (i >= -MAX_GRID && i <= MAX_GRID) {  // If not dock cell
+                               dc = primDCs[i >= primDCs.length ? primDCs.length - 1 : i];
+                       } else {
+                               dc = DOCK_DIM_CONSTRAINT;
+                       }
+
+                       ArrayList<LinkedDimGroup> groupList = new ArrayList<LinkedDimGroup>(4);
+                       groupLists[gIx++] = groupList;
+
+                       for (Integer ix : secIndexes) {
+                               Cell cell = isRows ? getCell(i, ix) : getCell(ix, i);
+                               if (cell == null || cell.compWraps.size() == 0)
+                                       continue;
+
+                               int span = (isRows ? cell.spany : cell.spanx);
+                               if (span > 1)
+                                       span = convertSpanToSparseGrid(i, span, primIndexes);
+
+                               boolean isPar = (cell.flowx == isRows);
+
+                               if ((!isPar && cell.compWraps.size() > 1) || span > 1) {
+
+                                       int linkType = isPar ? LinkedDimGroup.TYPE_PARALLEL : LinkedDimGroup.TYPE_SERIAL;
+                                       LinkedDimGroup lg = new LinkedDimGroup("p," + ix, span, linkType, !isRows, fromEnd);
+                                       lg.setCompWraps(cell.compWraps);
+                                       groupList.add(lg);
+                               } else {
+                                       for (int cwIx = 0; cwIx < cell.compWraps.size(); cwIx++) {
+                                               CompWrap cw = cell.compWraps.get(cwIx);
+                                               boolean rowBaselineAlign = (isRows && lc.isTopToBottom() && dc.getAlignOrDefault(!isRows) == UnitValue.BASELINE_IDENTITY); // Disable baseline for bottomToTop since I can not verify it working.
+                                               boolean isBaseline = isRows && cw.isBaselineAlign(rowBaselineAlign);
+
+                                               String linkCtx = isBaseline ? "baseline" : null;
+
+                                               // Find a group with same link context and put it in that group.
+                                               boolean foundList = false;
+                                               for (int glIx = 0, lastGl = groupList.size() - 1; glIx <= lastGl; glIx++) {
+                                                       LinkedDimGroup group = groupList.get(glIx);
+                                                       if (group.linkCtx == linkCtx || linkCtx != null && linkCtx.equals(group.linkCtx)) {
+                                                               group.addCompWrap(cw);
+                                                               foundList = true;
+                                                               break;
+                                                       }
+                                               }
+
+                                               // If none found and at last add a new group.
+                                               if (!foundList) {
+                                                       int linkType = isBaseline ? LinkedDimGroup.TYPE_BASELINE : LinkedDimGroup.TYPE_PARALLEL;
+                                                       LinkedDimGroup lg = new LinkedDimGroup(linkCtx, 1, linkType, !isRows, fromEnd);
+                                                       lg.addCompWrap(cw);
+                                                       groupList.add(lg);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return groupLists;
+       }
+
+       /** Spanning is specified in the uncompressed grid number. They can for instance be more than 60000 for the outer
+        * edge dock grid cells. When the grid is compressed and indexed after only the cells that area occupied the span
+        * is erratic. This method use the row/col indexes and corrects the span to be correct for the compressed grid.
+        * @param span The span in the uncompressed grid. <code>LayoutUtil.INF</code> will be interpreted to span the rest
+        * of the column/row excluding the surrounding docking components.
+        * @param indexes The indexes in the correct dimension.
+        * @return The converted span.
+        */
+       private static int convertSpanToSparseGrid(int curIx, int span, TreeSet<Integer> indexes)
+       {
+               int lastIx = curIx + span;
+               int retSpan = 1;
+
+               for (Integer ix : indexes) {
+                       if (ix <= curIx)
+                               continue;   // We have not arrived to the correct index yet
+
+                       if (ix >= lastIx)
+                               break;
+
+                       retSpan++;
+               }
+               return retSpan;
+       }
+
+       private boolean isCellFree(int r, int c, ArrayList<int[]> occupiedRects)
+       {
+               if (getCell(r, c) != null)
+                       return false;
+
+               for (int[] rect : occupiedRects) {
+                       if (rect[0] <= c && rect[1] <= r && rect[0] + rect[2] > c && rect[1] + rect[3] > r)
+                               return false;
+               }
+               return true;
+       }
+
+       private Cell getCell(int r, int c)
+       {
+               return grid.get((r << 16) + (c & 0xffff));
+       }
+
+       private void setCell(int r, int c, Cell cell)
+       {
+               if (c < 0 || r < 0)
+                       throw new IllegalArgumentException("Cell position cannot be negative. row: " + r + ", col: " + c);
+
+               if (c > MAX_GRID || r > MAX_GRID)
+                       throw new IllegalArgumentException("Cell position out of bounds. Out of cells. row: " + r + ", col: " + c);
+
+               rowIndexes.add(r);
+               colIndexes.add(c);
+
+               grid.put((r << 16) + (c & 0xffff), cell);
+       }
+
+       /** Adds a docking cell. That cell is outside the normal cell indexes.
+        * @param dockInsets The current dock insets. Will be updated!
+        * @param side top == 0, left == 1, bottom = 2, right = 3.
+        * @param cw The compwrap to put in a cell and add.
+        */
+       private void addDockingCell(int[] dockInsets, int side, CompWrap cw)
+       {
+               int r, c, spanx = 1, spany = 1;
+               switch (side) {
+                       case 0:
+                       case 2:
+                               r = side == 0 ? dockInsets[0]++ : dockInsets[2]--;
+                               c = dockInsets[1];
+                               spanx = dockInsets[3] - dockInsets[1] + 1;  // The +1 is for cell 0.
+                               colIndexes.add(dockInsets[3]); // Make sure there is a receiving cell
+                               break;
+
+                       case 1:
+                       case 3:
+                               c = side == 1 ? dockInsets[1]++ : dockInsets[3]--;
+                               r = dockInsets[0];
+                               spany = dockInsets[2] - dockInsets[0] + 1;  // The +1 is for cell 0.
+                               rowIndexes.add(dockInsets[2]); // Make sure there is a receiving cell
+                               break;
+
+                       default:
+                               throw new IllegalArgumentException("Internal error 123.");
+               }
+
+               rowIndexes.add(r);
+               colIndexes.add(c);
+
+               grid.put((r << 16) + (c & 0xffff), new Cell(cw, spanx, spany, spanx > 1));
+       }
+
+       /** A simple representation of a cell in the grid. Contains a number of component wraps, if they span more than one cell.
+        */
+       private static class Cell
+       {
+               private final int spanx, spany;
+               private final boolean flowx;
+               private final ArrayList<CompWrap> compWraps = new ArrayList<CompWrap>(2);
+
+               private boolean hasTagged = false;  // If one or more components have styles and need to be checked by the component sorter
+
+               private Cell(CompWrap cw)
+               {
+                       this(cw, 1, 1, true);
+               }
+
+               private Cell(int spanx, int spany, boolean flowx)
+               {
+                       this(null, spanx, spany, flowx);
+               }
+
+               private Cell(CompWrap cw, int spanx, int spany, boolean flowx)
+               {
+                       if (cw != null)
+                               compWraps.add(cw);
+                       this.spanx = spanx;
+                       this.spany = spany;
+                       this.flowx = flowx;
+               }
+       }
+
+       /** A number of component wraps that share a layout "something" <b>in one dimension</b>
+        */
+       private static class LinkedDimGroup
+       {
+               private static final int TYPE_SERIAL = 0;
+               private static final int TYPE_PARALLEL = 1;
+               private static final int TYPE_BASELINE = 2;
+
+               private final String linkCtx;
+               private final int span;
+               private final int linkType;
+               private final boolean isHor, fromEnd;
+
+               private final ArrayList<CompWrap> _compWraps = new ArrayList<CompWrap>(4);
+
+               private int lStart = 0, lSize = 0;  // Currently mostly for debug painting
+
+               private LinkedDimGroup(String linkCtx, int span, int linkType, boolean isHor, boolean fromEnd)
+               {
+                       this.linkCtx = linkCtx;
+                       this.span = span;
+                       this.linkType = linkType;
+                       this.isHor = isHor;
+                       this.fromEnd = fromEnd;
+               }
+
+               private void addCompWrap(CompWrap cw)
+               {
+                       _compWraps.add(cw);
+               }
+
+               private void setCompWraps(ArrayList<CompWrap> cws)
+               {
+                       if (_compWraps != cws) {
+                               _compWraps.clear();
+                               _compWraps.addAll(cws);
+                       }
+               }
+
+               private void layout(DimConstraint dc, int start, int size, int spanCount)
+               {
+                       lStart = start;
+                       lSize = size;
+
+                       if (_compWraps.isEmpty())
+                               return;
+
+                       ContainerWrapper parent = _compWraps.get(0).comp.getParent();
+                       if (linkType == TYPE_PARALLEL) {
+                               layoutParallel(parent, _compWraps, dc, start, size, isHor, fromEnd);
+                       } else if (linkType == TYPE_BASELINE) {
+                               layoutBaseline(parent, _compWraps, dc, start, size, LayoutUtil.PREF, spanCount);
+                       } else {
+                               layoutSerial(parent, _compWraps, dc, start, size, isHor, spanCount, fromEnd);
+                       }
+               }
+
+               /** Returns the min/pref/max sizes for this cell. Returned array <b>must not be altered</b>
+                * @return A shared min/pref/max array of sizes. Always of length 3 and never <code>null</code>. Will always be of type STATIC and PIXEL.
+                */
+               private int[] getMinPrefMax()
+               {
+                       int[] sizes = new int[3];
+                       if (!_compWraps.isEmpty()) {
+                               for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.PREF; sType++) {
+                                       if (linkType == TYPE_PARALLEL) {
+                                               sizes[sType] = getTotalSizeParallel(_compWraps, sType, isHor);
+                                       } else if (linkType == TYPE_BASELINE) {
+                                               AboveBelow aboveBelow = getBaselineAboveBelow(_compWraps, sType, false);
+                                               sizes[sType] = aboveBelow.sum();
+                                       } else {
+                                               sizes[sType] = getTotalSizeSerial(_compWraps, sType, isHor);
+                                       }
+                               }
+                               sizes[LayoutUtil.MAX] = LayoutUtil.INF;
+                       }
+                       return sizes;
+               }
+       }
+
+       /** Wraps a {@link java.awt.Component} together with its constraint. Caches a lot of information about the component so
+        * for instance not the preferred size has to be calculated more than once.
+        *
+        * Note! Does not ask the min/pref/max sizes again after the constructor. This means that
+        */
+       private final class CompWrap
+       {
+               private final ComponentWrapper comp;
+               private final CC cc;
+               private final int eHideMode;
+               private final boolean useVisualPadding;
+               private boolean sizesOk = false;
+               private boolean isAbsolute;
+
+               private int[][] gaps; // [top,left(actually before),bottom,right(actually after)][min,pref,max]
+
+               private final int[] horSizes = new int[3];
+               private final int[] verSizes = new int[3];
+
+               private int x = LayoutUtil.NOT_SET, y = LayoutUtil.NOT_SET, w = LayoutUtil.NOT_SET, h = LayoutUtil.NOT_SET;
+
+               private int forcedPushGaps = 0;   // 1 == before, 2 = after. Bitwise.
+
+               /**
+                * @param c
+                * @param cc
+                * @param eHideMode Effective hide mode. <= 0 means visible.
+                * @param useVisualPadding
+                */
+               private CompWrap(ComponentWrapper c, CC cc, int eHideMode, boolean useVisualPadding)
+               {
+                       this.comp = c;
+                       this.cc = cc;
+                       this.eHideMode = eHideMode;
+                       this.useVisualPadding = useVisualPadding;
+                       this.isAbsolute = cc.getHorizontal().getSize().isAbsolute() && cc.getVertical().getSize().isAbsolute();
+
+                       if (eHideMode > 1) {
+                               gaps = new int[4][];
+                               for (int i = 0; i < gaps.length; i++)
+                                       gaps[i] = new int[3];
+                       }
+               }
+
+               private int[] getSizes(boolean isHor)
+               {
+                       validateSize();
+                       return isHor ? horSizes : verSizes;
+               }
+
+               private void validateSize()
+               {
+                       BoundSize[] callbackSz = getCallbackSize(comp);
+
+                       if (isAbsolute && sizesOk && callbackSz == null)
+                               return;
+
+                       if (eHideMode <= 0) {
+                               int contentBias = comp.getContentBias();
+
+                               int sizeHint = contentBias == -1 ? -1 : (contentBias == 0 ? (w != LayoutUtil.NOT_SET ? w : comp.getWidth()) : (h != LayoutUtil.NOT_SET ? h : comp.getHeight()));
+
+                               BoundSize hBS = (callbackSz != null && callbackSz[0] != null) ? callbackSz[0] : cc.getHorizontal().getSize();
+                               BoundSize vBS = (callbackSz != null && callbackSz[1] != null) ? callbackSz[1] : cc.getVertical().getSize();
+
+                               for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+                                       switch (contentBias) {
+                                               case -1: // None
+                                               default:
+                                                       horSizes[i] = getSize(hBS, i, true, useVisualPadding, -1);
+                                                       verSizes[i] = getSize(vBS, i, false, useVisualPadding, -1);
+                                                       break;
+                                               case 0: // Hor
+                                                       horSizes[i] = getSize(hBS, i, true, useVisualPadding, -1);
+                                                       verSizes[i] = getSize(vBS, i, false, useVisualPadding, sizeHint > 0 ? sizeHint : horSizes[i]);
+                                                       break;
+                                               case 1: // Ver
+                                                       verSizes[i] = getSize(vBS, i, false, useVisualPadding, -1);
+                                                       horSizes[i] = getSize(hBS, i, true, useVisualPadding, sizeHint > 0 ? sizeHint : verSizes[i]);
+                                                       break;
+                                       }
+                               }
+
+                               correctMinMax(horSizes);
+                               correctMinMax(verSizes);
+                       } else {
+                               Arrays.fill(horSizes, 0); // Needed if component goes from visible -> invisible without recreating the grid.
+                               Arrays.fill(verSizes, 0);
+                       }
+                       sizesOk = true;
+               }
+
+               private int getSize(BoundSize uvs, int sizeType, boolean isHor, boolean useVP, int sizeHint)
+               {
+                       int size;
+                       if (uvs == null || uvs.getSize(sizeType) == null) {
+                               switch(sizeType) {
+                                       case LayoutUtil.MIN:
+                                               size = isHor ? comp.getMinimumWidth(sizeHint) : comp.getMinimumHeight(sizeHint);
+                                               break;
+                                       case LayoutUtil.PREF:
+                                               size = isHor ? comp.getPreferredWidth(sizeHint) : comp.getPreferredHeight(sizeHint);
+                                               break;
+                                       default:
+                                               size = isHor ? comp.getMaximumWidth(sizeHint) : comp.getMaximumHeight(sizeHint);
+                                               break;
+                               }
+                               if (useVP) {
+                                       //Do not include visual padding when calculating layout
+                                       int[] visualPadding = comp.getVisualPadding();
+
+                                       // Assume visualPadding is of length 4: top, left, bottom, right
+                                       if (visualPadding != null && visualPadding.length > 0)
+                                               size -= isHor ? (visualPadding[1] + visualPadding[3]) : (visualPadding[0] + visualPadding[2]);
+                               }
+                       } else {
+                               ContainerWrapper par = comp.getParent();
+                               float refValue = isHor ? par.getWidth() : par.getHeight();
+                               size = uvs.getSize(sizeType).getPixels(refValue, par, comp);
+                       }
+                       return size;
+               }
+
+
+               private void calcGaps(ComponentWrapper before, CC befCC, ComponentWrapper after, CC aftCC, String tag, boolean flowX, boolean isLTR)
+               {
+                       ContainerWrapper par = comp.getParent();
+                       int parW = par.getWidth();
+                       int parH = par.getHeight();
+
+                       BoundSize befGap = before != null ? (flowX ? befCC.getHorizontal() : befCC.getVertical()).getGapAfter() : null;
+                       BoundSize aftGap = after != null ? (flowX ? aftCC.getHorizontal() : aftCC.getVertical()).getGapBefore() : null;
+
+                       mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, befGap, (flowX ? null : before), tag, parH, 0, isLTR), false, true);
+                       mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, befGap, (flowX ? before : null), tag, parW, 1, isLTR), true, true);
+                       mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, aftGap, (flowX ? null : after), tag, parH, 2, isLTR), false, false);
+                       mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, aftGap, (flowX ? after : null), tag, parW, 3, isLTR), true, false);
+               }
+
+               private void setDimBounds(int start, int size, boolean isHor)
+               {
+                       if (isHor) {
+                               if (start != x || w != size) {
+                                       x = start;
+                                       w = size;
+                                       if (comp.getContentBias() == LayoutUtil.HORIZONTAL)
+                                               invalidateSizes(); // Only for components that have a bias the sizes will have changed.
+                               }
+                       } else {
+                               if (start != y || h != size) {
+                                       y = start;
+                                       h = size;
+                                       if (comp.getContentBias() == LayoutUtil.VERTICAL)
+                                               invalidateSizes(); // Only for components that have a bias the sizes will have changed.
+                               }
+                       }
+               }
+
+               void invalidateSizes()
+               {
+                       sizesOk = false;
+               }
+
+               private boolean isPushGap(boolean isHor, boolean isBefore)
+               {
+                       if (isHor && ((isBefore ? 1 : 2) & forcedPushGaps) != 0)
+                               return true;    // Forced
+
+                       DimConstraint dc = cc.getDimConstraint(isHor);
+                       BoundSize s = isBefore ? dc.getGapBefore() : dc.getGapAfter();
+                       return s != null && s.getGapPush();
+               }
+
+               /** Transfers the bounds to the component
+                */
+               private void transferBounds(boolean addVisualPadding)
+               {
+                       if (cc.isExternal())
+                               return;
+
+                       int compX = x;
+                       int compY = y;
+                       int compW = w;
+                       int compH = h;
+
+                       if (addVisualPadding) {
+                               //Add the visual padding back to the component when changing its size
+                               int[] visualPadding = comp.getVisualPadding();
+                               if (visualPadding != null) {
+                                       //assume visualPadding is of length 4: top, left, bottom, right
+                                       compX -= visualPadding[1];
+                                       compY -= visualPadding[0];
+                                       compW += (visualPadding[1] + visualPadding[3]);
+                                       compH += (visualPadding[0] + visualPadding[2]);
+                               }
+                       }
+
+                       comp.setBounds(compX, compY, compW, compH);
+               }
+
+               private void setForcedSizes(int[] sizes, boolean isHor)
+               {
+                       if (sizes == null)
+                               return;
+
+                       System.arraycopy(sizes, 0, getSizes(isHor), 0, 3);
+                       sizesOk = true;
+               }
+
+               private void setGaps(int[] minPrefMax, int ix)
+               {
+                       if (gaps == null)
+                               gaps = new int[][] {null, null, null, null};
+
+                       gaps[ix] = minPrefMax;
+               }
+
+               private void mergeGapSizes(int[] sizes, boolean isHor, boolean isTL)
+               {
+                       if (gaps == null)
+                               gaps = new int[][] {null, null, null, null};
+
+                       if (sizes == null)
+                               return;
+
+                       int gapIX = getGapIx(isHor, isTL);
+                       int[] oldGaps = gaps[gapIX];
+                       if (oldGaps == null) {
+                               oldGaps = new int[] {0, 0, LayoutUtil.INF};
+                               gaps[gapIX] = oldGaps;
+                       }
+
+                       oldGaps[LayoutUtil.MIN] = Math.max(sizes[LayoutUtil.MIN], oldGaps[LayoutUtil.MIN]);
+                       oldGaps[LayoutUtil.PREF] = Math.max(sizes[LayoutUtil.PREF], oldGaps[LayoutUtil.PREF]);
+                       oldGaps[LayoutUtil.MAX] = Math.min(sizes[LayoutUtil.MAX], oldGaps[LayoutUtil.MAX]);
+               }
+
+               private int getGapIx(boolean isHor, boolean isTL)
+               {
+                       return isHor ? (isTL ? 1 : 3) : (isTL ? 0 : 2);
+               }
+
+               private int getSizeInclGaps(int sizeType, boolean isHor)
+               {
+                       return filter(sizeType, getGapBefore(sizeType, isHor) + getSize(sizeType, isHor) + getGapAfter(sizeType, isHor));
+               }
+
+               private int getSize(int sizeType, boolean isHor)
+               {
+                       return filter(sizeType, getSizes(isHor)[sizeType]);
+               }
+
+               private int getGapBefore(int sizeType, boolean isHor)
+               {
+                       int[] gaps = getGaps(isHor, true);
+                       return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
+               }
+
+               private int getGapAfter(int sizeType, boolean isHor)
+               {
+                       int[] gaps = getGaps(isHor, false);
+                       return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
+               }
+
+               private int[] getGaps(boolean isHor, boolean isTL)
+               {
+                       return gaps[getGapIx(isHor, isTL)];
+               }
+
+               private int filter(int sizeType, int size)
+               {
+                       if (size == LayoutUtil.NOT_SET)
+                               return sizeType != LayoutUtil.MAX ? 0 : LayoutUtil.INF;
+                       return constrainSize(size);
+               }
+
+               private boolean isBaselineAlign(boolean defValue)
+               {
+                       Float g = cc.getVertical().getGrow();
+                       if (g != null && g.intValue() != 0)
+                               return false;
+
+                       UnitValue al = cc.getVertical().getAlign();
+                       return (al != null ? al == UnitValue.BASELINE_IDENTITY : defValue) && comp.hasBaseline();
+               }
+
+               private int getBaseline(int sizeType)
+               {
+                       return comp.getBaseline(getSize(sizeType, true), getSize(sizeType, false));
+               }
+
+               void adjustMinHorSizeUp(int minSize)
+               {
+                       int[] sz = getSizes(true);
+                       if (sz[LayoutUtil.MIN] < minSize)
+                               sz[LayoutUtil.MIN] = minSize;
+                       correctMinMax(sz);
+               }
+       }
+
+       //***************************************************************************************
+       //* Helper Methods
+       //***************************************************************************************
+
+       private static void layoutBaseline(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, int sizeType, int spanCount)
+       {
+               AboveBelow aboveBelow = getBaselineAboveBelow(compWraps, sizeType, true);
+               int blRowSize = aboveBelow.sum();
+
+               CC cc = compWraps.get(0).cc;
+
+               // Align for the whole baseline component array
+               UnitValue align = cc.getVertical().getAlign();
+               if (spanCount == 1 && align == null)
+                       align = dc.getAlignOrDefault(false);
+               if (align == UnitValue.BASELINE_IDENTITY)
+                       align = UnitValue.CENTER;
+
+               int offset = start + aboveBelow.maxAbove + (align != null ? Math.max(0, align.getPixels(size - blRowSize, parent, null)) : 0);
+               for (CompWrap cw : compWraps) {
+                       cw.y += offset;
+                       if (cw.y + cw.h > start + size)
+                               cw.h = start + size - cw.y;
+               }
+       }
+
+       private static void layoutSerial(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, int spanCount, boolean fromEnd)
+       {
+               FlowSizeSpec fss = mergeSizesGapsAndResConstrs(
+                               getComponentResizeConstraints(compWraps, isHor),
+                       getComponentGapPush(compWraps, isHor),
+                               getComponentSizes(compWraps, isHor),
+                               getGaps(compWraps, isHor));
+
+               Float[] pushW = dc.isFill() ? GROW_100 : null;
+               int[] sizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, pushW, LayoutUtil.PREF, size);
+               setCompWrapBounds(parent, sizes, compWraps, dc.getAlignOrDefault(isHor),  start, size, isHor, fromEnd);
+       }
+
+       private static void setCompWrapBounds(ContainerWrapper parent, int[] allSizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign,  int start, int size, boolean isHor, boolean fromEnd)
+       {
+               int totSize = LayoutUtil.sum(allSizes);
+               CC cc = compWraps.get(0).cc;
+               UnitValue align = correctAlign(cc, rowAlign, isHor, fromEnd);
+
+               int cSt = start;
+               int slack = size - totSize;
+               if (slack > 0 && align != null) {
+                       int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
+                       cSt += (fromEnd ? -al : al);
+               }
+
+               for (int i = 0, bIx = 0, iSz = compWraps.size(); i < iSz; i++) {
+                       CompWrap cw = compWraps.get(i);
+                       if (fromEnd ) {
+                               cSt -= allSizes[bIx++];
+                               cw.setDimBounds(cSt - allSizes[bIx], allSizes[bIx], isHor);
+                               cSt -= allSizes[bIx++];
+                       } else {
+                               cSt += allSizes[bIx++];
+                               cw.setDimBounds(cSt, allSizes[bIx], isHor);
+                               cSt += allSizes[bIx++];
+                       }
+               }
+       }
+
+       private static void layoutParallel(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, boolean fromEnd)
+       {
+               int[][] sizes = new int[compWraps.size()][];    // [compIx][gapBef,compSize,gapAft]
+
+               for (int i = 0; i < sizes.length; i++) {
+                       CompWrap cw = compWraps.get(i);
+
+                       DimConstraint cDc = cw.cc.getDimConstraint(isHor);
+
+                       ResizeConstraint[] resConstr = new ResizeConstraint[] {
+                                       cw.isPushGap(isHor, true) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
+                                       cDc.resize,
+                                       cw.isPushGap(isHor, false) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
+                       };
+
+                       int[][] sz = new int[][] {
+                               cw.getGaps(isHor, true), cw.getSizes(isHor), cw.getGaps(isHor, false)
+                       };
+
+                       Float[] pushW = dc.isFill() ? GROW_100 : null;
+
+                       sizes[i] = LayoutUtil.calculateSerial(sz, resConstr, pushW, LayoutUtil.PREF, size);
+               }
+
+               UnitValue rowAlign = dc.getAlignOrDefault(isHor);
+               setCompWrapBounds(parent, sizes, compWraps, rowAlign, start, size, isHor, fromEnd);
+       }
+
+       private static void setCompWrapBounds(ContainerWrapper parent, int[][] sizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign,  int start, int size, boolean isHor, boolean fromEnd)
+       {
+               for (int i = 0; i < sizes.length; i++) {
+                       CompWrap cw = compWraps.get(i);
+
+                       UnitValue align = correctAlign(cw.cc, rowAlign, isHor, fromEnd);
+
+                       int[] cSizes = sizes[i];
+                       int gapBef = cSizes[0];
+                       int cSize = cSizes[1];  // No Math.min(size, cSizes[1]) here!
+                       int gapAft = cSizes[2];
+
+                       int cSt = fromEnd ? start - gapBef : start + gapBef;
+                       int slack = size - cSize - gapBef - gapAft;
+                       if (slack > 0 && align != null) {
+                               int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
+                               cSt += (fromEnd ? -al : al);
+                       }
+
+                       cw.setDimBounds(fromEnd ? cSt - cSize : cSt, cSize, isHor);
+               }
+       }
+
+       private static UnitValue correctAlign(CC cc, UnitValue rowAlign, boolean isHor, boolean fromEnd)
+       {
+               UnitValue align = (isHor ? cc.getHorizontal() : cc.getVertical()).getAlign();
+               if (align == null)
+                       align = rowAlign;
+               if (align == UnitValue.BASELINE_IDENTITY)
+                       align = UnitValue.CENTER;
+
+               if (fromEnd) {
+                       if (align == UnitValue.LEFT)
+                               align = UnitValue.RIGHT;
+                       else if (align == UnitValue.RIGHT)
+                               align = UnitValue.LEFT;
+               }
+               return align;
+       }
+
+    private static class AboveBelow {
+           int maxAbove;
+           int maxBelow;
+
+           AboveBelow(int maxAbove, int maxBelow) {
+            this.maxAbove = maxAbove;
+            this.maxBelow = maxBelow;
+        }
+
+        int sum() {
+               return maxAbove + maxBelow;
+        }
+    }
+
+       private static AboveBelow getBaselineAboveBelow(ArrayList<CompWrap> compWraps, int sType, boolean centerBaseline)
+       {
+               int maxAbove = Integer.MIN_VALUE;
+               int maxBelow = Integer.MIN_VALUE;
+               for (CompWrap cw : compWraps) {
+                       int height = cw.getSize(sType, false);
+                       if (height >= LayoutUtil.INF)
+                               return new AboveBelow(LayoutUtil.INF / 2, LayoutUtil.INF / 2);
+
+                       int baseline = cw.getBaseline(sType);
+                       int above = baseline + cw.getGapBefore(sType, false);
+                       maxAbove = Math.max(above, maxAbove);
+                       maxBelow = Math.max(height - baseline + cw.getGapAfter(sType, false), maxBelow);
+
+                       if (centerBaseline)
+                               cw.setDimBounds(-baseline, height, false);
+               }
+               return new AboveBelow(maxAbove, maxBelow);
+       }
+
+       private static int getTotalSizeParallel(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
+       {
+               int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
+
+               for (CompWrap cw : compWraps) {
+                       int cwSize = cw.getSizeInclGaps(sType, isHor);
+                       if (cwSize >= LayoutUtil.INF)
+                               return LayoutUtil.INF;
+
+                       if (sType == LayoutUtil.MAX ? cwSize < size : cwSize > size)
+                               size = cwSize;
+               }
+               return constrainSize(size);
+       }
+
+       private static int getTotalSizeSerial(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
+       {
+               int totSize = 0;
+               for (int i = 0, iSz = compWraps.size(), lastGapAfter = 0; i < iSz; i++) {
+                       CompWrap wrap = compWraps.get(i);
+                       int gapBef = wrap.getGapBefore(sType, isHor);
+                       if (gapBef > lastGapAfter)
+                               totSize += gapBef - lastGapAfter;
+
+                       totSize += wrap.getSize(sType, isHor);
+                       totSize += (lastGapAfter = wrap.getGapAfter(sType, isHor));
+
+                       if (totSize >= LayoutUtil.INF)
+                               return LayoutUtil.INF;
+               }
+               return constrainSize(totSize);
+       }
+
+       private static int getTotalGroupsSizeParallel(ArrayList<LinkedDimGroup> groups, int sType, boolean countSpanning)
+       {
+               int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
+               for (LinkedDimGroup group : groups) {
+                       if (countSpanning || group.span == 1) {
+                               int grpSize = group.getMinPrefMax()[sType];
+                               if (grpSize >= LayoutUtil.INF)
+                                       return LayoutUtil.INF;
+
+                               if (sType == LayoutUtil.MAX ? grpSize < size : grpSize > size)
+                                       size = grpSize;
+                       }
+               }
+               return constrainSize(size);
+       }
+
+       /**
+        * @param compWraps
+        * @param isHor
+        * @return Might contain LayoutUtil.NOT_SET
+        */
+       private static int[][] getComponentSizes(ArrayList<CompWrap> compWraps, boolean isHor)
+       {
+               int[][] compSizes = new int[compWraps.size()][];
+               for (int i = 0; i < compSizes.length; i++)
+                       compSizes[i] = compWraps.get(i).getSizes(isHor);
+               return compSizes;
+       }
+
+       /** Merges sizes and gaps together with Resize Constraints. For gaps {@link #GAP_RC_CONST} is used.
+        * @param resConstr One resize constraint for every row/component. Can be lesser in length and the last element should be used for missing elements.
+        * @param gapPush If the corresponding gap should be considered pushing and thus want to take free space if left over. Should be one more than resConstrs!
+        * @param minPrefMaxSizes The sizes (min/pref/max) for every row/component.
+        * @param gapSizes The gaps before and after each row/component packed in one double sized array.
+        * @return A holder for the merged values.
+        */
+       private static FlowSizeSpec mergeSizesGapsAndResConstrs(ResizeConstraint[] resConstr, boolean[] gapPush, int[][] minPrefMaxSizes, int[][] gapSizes)
+       {
+               int[][] sizes = new int[(minPrefMaxSizes.length << 1) + 1][];  // Make room for gaps around.
+               ResizeConstraint[] resConstsInclGaps = new ResizeConstraint[sizes.length];
+
+               sizes[0] = gapSizes[0];
+               for (int i = 0, crIx = 1; i < minPrefMaxSizes.length; i++, crIx += 2) {
+
+                       // Component bounds and constraints
+                       resConstsInclGaps[crIx] = resConstr[i];
+                       sizes[crIx] = minPrefMaxSizes[i];
+
+                       sizes[crIx + 1] = gapSizes[i + 1];
+
+                       if (sizes[crIx - 1] != null)
+                               resConstsInclGaps[crIx - 1] = gapPush[i < gapPush.length ? i : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
+
+                       if (i == (minPrefMaxSizes.length - 1) && sizes[crIx + 1] != null)
+                               resConstsInclGaps[crIx + 1] = gapPush[(i + 1) < gapPush.length ? (i + 1) : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
+               }
+
+               // Check for null and set it to 0, 0, 0.
+               for (int i = 0; i < sizes.length; i++) {
+                       if (sizes[i] == null)
+                               sizes[i] = new int[3];
+               }
+
+               return new FlowSizeSpec(sizes, resConstsInclGaps);
+       }
+
+       private static int[] mergeSizes(int[] oldValues, int[] newValues)
+       {
+               if (oldValues == null)
+                       return newValues;
+
+               if (newValues == null)
+                       return oldValues;
+
+               int[] ret = new int[oldValues.length];
+               for (int i = 0; i < ret.length; i++)
+                       ret[i] = mergeSizes(oldValues[i], newValues[i], true);
+
+               return ret;
+       }
+
+       private static int mergeSizes(int oldValue, int newValue, boolean toMax)
+       {
+               if (oldValue == LayoutUtil.NOT_SET || oldValue == newValue)
+                       return newValue;
+
+               if (newValue == LayoutUtil.NOT_SET)
+                       return oldValue;
+
+               return toMax != oldValue > newValue ? newValue : oldValue;
+       }
+
+       private static int constrainSize(int s)
+       {
+               return s > 0 ? (s < LayoutUtil.INF ? s : LayoutUtil.INF) : 0;
+       }
+
+       private static void correctMinMax(int s[])
+       {
+               if (s[LayoutUtil.MIN] > s[LayoutUtil.MAX])
+                       s[LayoutUtil.MIN] = s[LayoutUtil.MAX];  // Since MAX is almost always explicitly set use that
+
+               if (s[LayoutUtil.PREF] < s[LayoutUtil.MIN])
+                       s[LayoutUtil.PREF] = s[LayoutUtil.MIN];
+
+               if (s[LayoutUtil.PREF] > s[LayoutUtil.MAX])
+                       s[LayoutUtil.PREF] = s[LayoutUtil.MAX];
+       }
+
+       private static final class FlowSizeSpec
+       {
+               private final int[][] sizes;  // [row/col index][min, pref, max]
+               private final ResizeConstraint[] resConstsInclGaps;  // [row/col index]
+
+               private FlowSizeSpec(int[][] sizes, ResizeConstraint[] resConstsInclGaps)
+               {
+                       this.sizes = sizes;
+                       this.resConstsInclGaps = resConstsInclGaps;
+               }
+
+               /**
+                * @param specs The specs for the columns or rows. Last index will be used of <code>fromIx + len</code> is greater than this array's length.
+                * @param targetSize The size to try to meet.
+                * @param defGrow The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+                * @param fromIx
+                * @param len
+                * @param sizeType
+                * @param eagerness How eager the algorithm should be to try to expand the sizes.
+                * <ul>
+                * <li>0 - Grow only rows/columns which have the <code>sizeType</code> set to be the containing components AND which has a grow weight &gt; 0.
+                * <li>1 - Grow only rows/columns which have the <code>sizeType</code> set to be the containing components AND which has a grow weight &gt; 0 OR unspecified.
+                * <li>2 - Grow all rows/columns that have a grow weight &gt; 0.
+                * <li>3 - Grow all rows/columns that have a grow weight &gt; 0 OR unspecified.
+                * </ul>
+                * @return The new size.
+                */
+               private int expandSizes(DimConstraint[] specs, Float[] defGrow, int targetSize, int fromIx, int len, int sizeType, int eagerness)
+               {
+                       ResizeConstraint[] resConstr = new ResizeConstraint[len];
+                       int[][] sizesToExpand = new int[len][];
+                       for (int i = 0; i < len; i++) {
+                               int[] minPrefMax = sizes[i + fromIx];
+                               sizesToExpand[i] = new int[] {minPrefMax[sizeType], minPrefMax[LayoutUtil.PREF], minPrefMax[LayoutUtil.MAX]};
+
+                               if (eagerness <= 1 && i % 2 == 0) { // (i % 2 == 0) means only odd indexes, which is only rows/col indexes and not gaps.
+                                       int cIx = (i + fromIx - 1) >> 1;
+                                       DimConstraint spec = (DimConstraint) LayoutUtil.getIndexSafe(specs, cIx);
+
+                                       BoundSize sz = spec.getSize();
+                                       if (    (sizeType == LayoutUtil.MIN && sz.getMin() != null && sz.getMin().getUnit() != UnitValue.MIN_SIZE) ||
+                                               (sizeType == LayoutUtil.PREF && sz.getPreferred() != null && sz.getPreferred().getUnit() != UnitValue.PREF_SIZE)) {
+                                               continue;
+                                       }
+                               }
+                               resConstr[i] = (ResizeConstraint) LayoutUtil.getIndexSafe(resConstsInclGaps, i + fromIx);
+                       }
+
+                       Float[] growW = (eagerness == 1 || eagerness == 3) ? extractSubArray(specs, defGrow, fromIx, len): null;
+                       int[] newSizes = LayoutUtil.calculateSerial(sizesToExpand, resConstr, growW, LayoutUtil.PREF, targetSize);
+                       int newSize = 0;
+
+                       for (int i = 0; i < len; i++) {
+                               int s = newSizes[i];
+                               sizes[i + fromIx][sizeType] = s;
+                               newSize += s;
+                       }
+                       return newSize;
+               }
+       }
+
+       private static Float[] extractSubArray(DimConstraint[] specs, Float[] arr, int ix, int len)
+       {
+               if (arr == null || arr.length < ix + len) {
+                       Float[] growLastArr = new Float[len];
+
+                       // Handle a group where some rows (first one/few and/or last one/few) are docks.
+                       for (int i = ix + len - 1; i >= 0; i -= 2) {
+                               int specIx = (i >> 1);
+                               if (specs[specIx] != DOCK_DIM_CONSTRAINT) {
+                                       growLastArr[i - ix] = ResizeConstraint.WEIGHT_100;
+                                       return growLastArr;
+                               }
+                       }
+                       return growLastArr;
+               }
+
+               Float[] newArr = new Float[len];
+        System.arraycopy(arr, ix, newArr, 0, len);
+               return newArr;
+       }
+
+       private static WeakHashMap<Object, int[][]>[] PARENT_ROWCOL_SIZES_MAP = null;
+       @SuppressWarnings( "unchecked" )
+       private static synchronized void putSizesAndIndexes(Object parComp, int[] sizes, int[] ixArr, boolean isRows)
+       {
+               if (PARENT_ROWCOL_SIZES_MAP == null)    // Lazy since only if designing in IDEs
+                       PARENT_ROWCOL_SIZES_MAP = new WeakHashMap[] {new WeakHashMap<Object,int[][]>(4), new WeakHashMap<Object,int[][]>(4)};
+
+               PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].put(parComp, new int[][]{ixArr, sizes});
+       }
+
+       static synchronized int[][] getSizesAndIndexes(Object parComp, boolean isRows)
+       {
+               if (PARENT_ROWCOL_SIZES_MAP == null)
+                       return null;
+
+               return PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].get(parComp);
+       }
+
+       private static WeakHashMap<Object, ArrayList<WeakCell>> PARENT_GRIDPOS_MAP = null;
+       private static synchronized void saveGrid(ComponentWrapper parComp, LinkedHashMap<Integer, Cell> grid)
+       {
+               if (PARENT_GRIDPOS_MAP == null)    // Lazy since only if designing in IDEs
+                       PARENT_GRIDPOS_MAP = new WeakHashMap<Object, ArrayList<WeakCell>>(4);
+
+               ArrayList<WeakCell> weakCells = new ArrayList<WeakCell>(grid.size());
+
+               for (Map.Entry<Integer, Cell> e : grid.entrySet()) {
+                       Cell cell = e.getValue();
+                       Integer xyInt = e.getKey();
+                       if (xyInt != null) {
+                               int x = (xyInt << 16) >> 16;
+                               int y = xyInt >> 16;
+
+                               for (CompWrap cw : cell.compWraps)
+                                       weakCells.add(new WeakCell(cw.comp.getComponent(), x, y, cell.spanx, cell.spany));
+                       }
+               }
+
+               PARENT_GRIDPOS_MAP.put(parComp.getComponent(), weakCells);
+       }
+
+       static synchronized HashMap<Object, int[]> getGridPositions(Object parComp)
+       {
+               ArrayList<WeakCell> weakCells = PARENT_GRIDPOS_MAP != null ? PARENT_GRIDPOS_MAP.get(parComp) : null;
+               if (weakCells == null)
+                       return null;
+
+               HashMap<Object, int[]> retMap = new HashMap<Object, int[]>();
+
+               for (WeakCell wc : weakCells) {
+                       Object component = wc.componentRef.get();
+                       if (component != null)
+                               retMap.put(component, new int[] {wc.x, wc.y, wc.spanX, wc.spanY});
+               }
+
+               return retMap;
+       }
+
+       private static class WeakCell
+       {
+               private final WeakReference<Object> componentRef;
+               private final int x, y, spanX, spanY;
+
+               private WeakCell(Object component, int x, int y, int spanX, int spanY)
+               {
+                       this.componentRef = new WeakReference<Object>(component);
+                       this.x = x;
+                       this.y = y;
+                       this.spanX = spanX;
+                       this.spanY = spanY;
+               }
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/InCellGapProvider.java b/srcjar/net/miginfocom/layout/InCellGapProvider.java
new file mode 100644 (file)
index 0000000..bccf60c
--- /dev/null
@@ -0,0 +1,67 @@
+package net.miginfocom.layout;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** An interface to implement if you want to decide the gaps between two types of components within the same cell.
+ * <p>
+ * E.g.:
+ *
+ * <pre>
+ * {@code
+ * if (adjacentComp == null || adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.TOP)
+ *       return null;
+ *
+ * boolean isHor = (adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.RIGHT);
+ *
+ * if (adjacentComp.getComponentType(false) == ComponentWrapper.TYPE_LABEL && comp.getComponentType(false) == ComponentWrapper.TYPE_TEXT_FIELD)
+ *    return isHor ? UNRELATED_Y : UNRELATED_Y;
+ *
+ * return (adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.RIGHT) ? RELATED_X : RELATED_Y;
+ * }
+ * </pre>
+ */
+public interface InCellGapProvider
+{
+       /** Returns the default gap between two components that <b>are in the same cell</b>.
+        * @param comp The component that the gap is for. Never <code>null</code>.
+        * @param adjacentComp The adjacent component if any. May be <code>null</code>.
+        * @param adjacentSide What side the <code>adjacentComp</code> is on. {@link javax.swing.SwingUtilities#TOP} or
+        * {@link javax.swing.SwingUtilities#LEFT} or {@link javax.swing.SwingUtilities#BOTTOM} or {@link javax.swing.SwingUtilities#RIGHT}.
+        * @param tag The tag string that the component might be tagged with in the component constraints. May be <code>null</code>.
+        * @param isLTR If it is left-to-right.
+        * @return The default gap between two components or <code>null</code> if there should be no gap.
+        */
+       public abstract BoundSize getDefaultGap(ComponentWrapper comp, ComponentWrapper adjacentComp, int adjacentSide, String tag, boolean isLTR);
+}
diff --git a/srcjar/net/miginfocom/layout/LC.java b/srcjar/net/miginfocom/layout/LC.java
new file mode 100644 (file)
index 0000000..b40fa75
--- /dev/null
@@ -0,0 +1,1044 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** Contains the constraints for an instance of the {@link LC} layout manager.
+ */
+public final class LC implements Externalizable
+{
+       // See the corresponding set/get method for documentation of the property!
+
+       private int wrapAfter = LayoutUtil.INF;
+
+       private Boolean leftToRight = null;
+
+       private UnitValue[] insets = null;    // Never null elements but if unset array is null
+
+       private UnitValue alignX = null, alignY = null;
+
+       private BoundSize gridGapX = null, gridGapY = null;
+
+       private BoundSize width = BoundSize.NULL_SIZE, height = BoundSize.NULL_SIZE;
+
+       private BoundSize packW = BoundSize.NULL_SIZE, packH = BoundSize.NULL_SIZE;
+
+       private float pwAlign = 0.5f, phAlign = 1.0f;
+
+       private int debugMillis = 0;
+
+       private int hideMode = 0;
+
+       private boolean noCache = false;
+
+       private boolean flowX = true;
+
+       private boolean fillX = false, fillY = false;
+
+       private boolean topToBottom = true;
+
+       private boolean noGrid = false;
+
+       private boolean visualPadding = true;
+
+       /** Empty constructor.
+        */
+       public LC()
+       {
+       }
+
+       // ************************************************************************
+       // * JavaBean get/set methods.
+       // ************************************************************************
+
+
+       /** If components have sizes or positions linked to the bounds of the parent in some way (as for instance the <code>"%"</code> unit has) the cache
+        * must be turned off for the panel. If components does not get the correct or expected size or position try to set this property to <code>true</code>.
+        * @return <code>true</code> means no cache and slightly slower layout.
+        */
+       public boolean isNoCache()
+       {
+               return noCache;
+       }
+
+       /** If components have sizes or positions linked to the bounds of the parent in some way (as for instance the <code>"%"</code> unit has) the cache
+        * must be turned off for the panel. If components does not get the correct or expected size or position try to set this property to <code>true</code>.
+        * @param b <code>true</code> means no cache and slightly slower layout.
+        */
+       public void setNoCache(boolean b)
+       {
+               this.noCache = b;
+       }
+
+       /** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+        * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+        * by this property.
+        * @return The current alignment.
+        */
+       public final UnitValue getAlignX()
+       {
+               return alignX;
+       }
+
+       /** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+        * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+        * by this property.
+        * @param uv The new alignment. Use {@link ConstraintParser#parseAlignKeywords(String, boolean)} to create the {@link UnitValue}. May be <code>null</code>.
+        */
+       public final void setAlignX(UnitValue uv)
+       {
+               this.alignX = uv;
+       }
+
+       /** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+        * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+        * by this property.
+        * @return The current alignment.
+        */
+       public final UnitValue getAlignY()
+       {
+               return alignY;
+       }
+
+       /** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+        * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+        * by this property.
+        * @param uv The new alignment. Use {@link ConstraintParser#parseAlignKeywords(String, boolean)} to create the {@link UnitValue}. May be <code>null</code>.
+        */
+       public final void setAlignY(UnitValue uv)
+       {
+               this.alignY = uv;
+       }
+
+       /** If <code>&gt; 0</code> the debug decorations will be repainted every <code>millis</code>. No debug information if <code>&lt;= 0</code> (default).
+        * @return The current debug repaint interval.
+        */
+       public final int getDebugMillis()
+       {
+               return debugMillis;
+       }
+
+       /** If <code>&gt; 0</code> the debug decorations will be repainted every <code>millis</code>. No debug information if <code>&lt;= 0</code> (default).
+        * @param millis The new debug repaint interval.
+        */
+       public final void setDebugMillis(int millis)
+       {
+               this.debugMillis = millis;
+       }
+
+       /** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+        * @return <code>true</code> means fill. <code>false</code> is default.
+        */
+       public final boolean isFillX()
+       {
+               return fillX;
+       }
+
+       /** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+        * @param b <code>true</code> means fill. <code>false</code> is default.
+        */
+       public final void setFillX(boolean b)
+       {
+               this.fillX = b;
+       }
+
+       /** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+        * @return <code>true</code> means fill. <code>false</code> is default.
+        */
+       public final boolean isFillY()
+       {
+               return fillY;
+       }
+
+       /** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+        * @param b <code>true</code> means fill. <code>false</code> is default.
+        */
+       public final void setFillY(boolean b)
+       {
+               this.fillY = b;
+       }
+
+       /** The default flow direction. Normally (which is <code>true</code>) this is horizontal and that means that the "next" component
+        * will be put in the cell to the right (or to the left if left-to-right is false).
+        * @return <code>true</code> is the default flow horizontally.
+        * @see #setLeftToRight(Boolean)
+        */
+       public final boolean isFlowX()
+       {
+               return flowX;
+       }
+
+       /** The default flow direction. Normally (which is <code>true</code>) this is horizontal and that means that the "next" component
+        * will be put in the cell to the right (or to the left if left-to-right is false).
+        * @param b <code>true</code> is the default flow horizontally.
+        * @see #setLeftToRight(Boolean)
+        */
+       public final void setFlowX(boolean b)
+       {
+               this.flowX = b;
+       }
+
+       /** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the columns in the grid.
+        * @return The default grid gap between columns in the grid. <code>null</code> if the platform default is used.
+        */
+       public final BoundSize getGridGapX()
+       {
+               return gridGapX;
+       }
+
+       /** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the columns in the grid.
+        * @param x The default grid gap between columns in the grid. If <code>null</code> the platform default is used.
+        */
+       public final void setGridGapX(BoundSize x)
+       {
+               this.gridGapX = x;
+       }
+
+       /** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the rows in the grid.
+        * @return The default grid gap between rows in the grid. <code>null</code> if the platform default is used.
+        */
+       public final BoundSize getGridGapY()
+       {
+               return gridGapY;
+       }
+
+       /** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the rows in the grid.
+        * @param y The default grid gap between rows in the grid. If <code>null</code> the platform default is used.
+        */
+       public final void setGridGapY(BoundSize y)
+       {
+               this.gridGapY = y;
+       }
+
+       /** How a component that is hidden (not visible) should be treated by default.
+        * @return The mode:<br>
+        * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+        * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+        * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+        * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+        */
+       public final int getHideMode()
+       {
+               return hideMode;
+       }
+
+       /** How a component that is hidden (not visible) should be treated.
+        * @param mode The mode:<br>
+        * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+        * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+        * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+        * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+        */
+       public final void setHideMode(int mode)
+       {
+               if (mode < 0 || mode > 3)
+                       throw new IllegalArgumentException("Wrong hideMode: " + mode);
+
+               this.hideMode = mode;
+       }
+
+       /** The insets for the layed out panel. The insets will be an empty space around the components in the panel. <code>null</code> values
+        * means that the default panel insets for the platform is used. See {@link PlatformDefaults#setDialogInsets(net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue)}.
+        * @return The insets. Of length 4 (top, left, bottom, right) or <code>null</code>. The elements (1 to 4) may be <code>null</code>. The array is a copy and can be used freely.
+        * @see net.miginfocom.layout.ConstraintParser#parseInsets(String, boolean)
+        */
+       public final UnitValue[] getInsets()
+       {
+               return insets != null ? new UnitValue[] {insets[0], insets[1], insets[2], insets[3]} : null;
+       }
+
+       /** The insets for the layed out panel. The insets will be an empty space around the components in the panel. <code>null</code> values
+        * means that the default panel insets for the platform is used. See {@link PlatformDefaults#setDialogInsets(net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue)}.
+        * @param ins The new insets. Must be of length 4 (top, left, bottom, right) or <code>null</code>. The elements (1 to 4) may be <code>null</code> to use
+        * the platform default for that side. The array is copied for storage.
+        * @see net.miginfocom.layout.ConstraintParser#parseInsets(String, boolean)
+        */
+       public final void setInsets(UnitValue[] ins)
+       {
+               this.insets = ins != null ? new UnitValue[] {ins[0], ins[1], ins[2], ins[3]} : null;
+       }
+
+       /** If the layout should be forced to be left-to-right or right-to-left. A value of <code>null</code> is default and
+        * means that this will be picked up from the {@link java.util.Locale} that the container being layed out is reporting.
+        * @return <code>Boolean.TRUE</code> if force left-to-right. <code>Boolean.FALSE</code> if force tight-to-left. <code>null</code>
+        * for the default "let the current Locale decide".
+        */
+       public final Boolean getLeftToRight()
+       {
+               return leftToRight;
+       }
+
+       /** If the layout should be forced to be left-to-right or right-to-left. A value of <code>null</code> is default and
+        * means that this will be picked up from the {@link java.util.Locale} that the container being layed out is reporting.
+        * @param b <code>Boolean.TRUE</code> to force left-to-right. <code>Boolean.FALSE</code> to force tight-to-left. <code>null</code>
+        * for the default "let the current Locale decide".
+        */
+       public final void setLeftToRight(Boolean b)
+       {
+               this.leftToRight = b;
+       }
+
+       /** If the whole layout should be non grid based. It is the same as setting the "nogrid" property on every row/column in the grid.
+        * @return <code>true</code> means not grid based. <code>false</code> is default.
+        */
+       public final boolean isNoGrid()
+       {
+               return noGrid;
+       }
+
+       /** If the whole layout should be non grid based. It is the same as setting the "nogrid" property on every row/column in the grid.
+        * @param b <code>true</code> means no grid. <code>false</code> is default.
+        */
+       public final void setNoGrid(boolean b)
+       {
+               this.noGrid = b;
+       }
+
+       /** If the layout should go from the default top-to-bottom in the grid instead of the optional bottom-to-top.
+        * @return <code>true</code> for the default top-to-bottom.
+        */
+       public final boolean isTopToBottom()
+       {
+               return topToBottom;
+       }
+
+       /** If the layout should go from the default top-to-bottom in the grid instead of the optional bottom-to-top.
+        * @param b <code>true</code> for the default top-to-bottom.
+        */
+       public final void setTopToBottom(boolean b)
+       {
+               this.topToBottom = b;
+       }
+
+       /** If visual padding should be automatically used and compensated for by this layout instance.
+        * @return <code>true</code> if visual padding.
+        */
+       public final boolean isVisualPadding()
+       {
+               return visualPadding;
+       }
+
+       /** If visual padding should be automatically used and compensated for by this layout instance.
+        * @param b <code>true</code> turns on visual padding.
+        */
+       public final void setVisualPadding(boolean b)
+       {
+               this.visualPadding = b;
+       }
+
+       /** Returns after what cell the grid should always auto wrap.
+        * @return After what cell the grid should always auto wrap. If <code>0</code> the number of columns/rows in the
+        * {@link net.miginfocom.layout.AC} is used. <code>LayoutUtil.INF</code> is used for no auto wrap.
+        */
+       public final int getWrapAfter()
+       {
+               return wrapAfter;
+       }
+
+       /** Sets after what cell the grid should always auto wrap.
+        * @param count After what cell the grid should always auto wrap. If <code>0</code> the number of columns/rows in the
+        * {@link net.miginfocom.layout.AC} is used. <code>LayoutUtil.INF</code> is used for no auto wrap.
+        */
+       public final void setWrapAfter(int count)
+       {
+               this.wrapAfter = count;
+       }
+
+       /** Returns the "pack width" for the <b>window</b> that this container is located in. When the size of this container changes
+        * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+        * as well as the size window should optimally get. This optimal size is normally its "preferred" size which is why "preferred"
+        * is the normal value to set here.
+        * <p>
+        * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+        * <p>
+        * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+        * @return The current value. Never <code>null</code>. Check if not set with <code>.isUnset()</code>.
+        * @since 3.5
+        */
+       public final BoundSize getPackWidth()
+       {
+               return packW;
+       }
+
+       /** Sets the "pack width" for the <b>window</b> that this container is located in. When the size of this container changes
+        * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+        * as well as the size window should optimally get. This optimal size is normally its "preferred" size which is why "preferred"
+        * is the normal value to set here.
+        * <p>
+        * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+        * <p>
+        * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+        * @param size The new pack size. If <code>null</code> it will be corrected to an "unset" BoundSize.
+        * @since 3.5
+        */
+       public final void setPackWidth(BoundSize size)
+       {
+               packW = size != null ? size : BoundSize.NULL_SIZE;
+       }
+
+       /** Returns the "pack height" for the <b>window</b> that this container is located in. When the size of this container changes
+        * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+        * as well as the size window should optimally get. This optimal size is normally its "preferred" size which is why "preferred"
+        * is the normal value to set here.
+        * <p>
+        * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+        * <p>
+        * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+        * @return The current value. Never <code>null</code>. Check if not set with <code>.isUnset()</code>.
+        * @since 3.5
+        */
+       public final BoundSize getPackHeight()
+       {
+               return packH;
+       }
+
+       /** Sets the "pack height" for the <b>window</b> that this container is located in. When the size of this container changes
+        * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+        * as well as the size window should optimally get. This optimal size is normally its "preferred" size which is why "preferred"
+        * is the normal value to set here.
+        * <p>
+        * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+        * <p>
+        * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+        * @param size The new pack size. If <code>null</code> it will be corrected to an "unset" BoundSize.
+        * @since 3.5
+        */
+       public final void setPackHeight(BoundSize size)
+       {
+               packH = size != null ? size : BoundSize.NULL_SIZE;
+       }
+
+
+       /** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+        * decides where the extra/superfluous size is placed. 0f means that the window will resize so that the upper part moves up and the
+        * lower side stays in the same place. 0.5f will expand/reduce the window equally upwards and downwards. 1f will do the opposite of 0f
+        * of course.
+        * @return The pack alignment. Always between 0f and 1f, inclusive.
+        * @since 3.5
+        */
+       public final float getPackHeightAlign()
+       {
+               return phAlign;
+       }
+
+       /** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+        * decides where the extra/superfluous size is placed. 0f means that the window will resize so that the upper part moves up and the
+        * lower side stays in the same place. 0.5f will expand/reduce the window equally upwards and downwards. 1f will do the opposite of 0f
+        * of course.
+        * @param align The pack alignment. Always between 0f and 1f, inclusive. Values outside this will be truncated.
+        * @since 3.5
+        */
+       public final void setPackHeightAlign(float align)
+       {
+               phAlign = Math.max(0f, Math.min(1f, align));
+       }
+
+       /** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+        * decides where the extra/superfluous size is placed. 0f means that the window will resize so that the left part moves left and the
+        * right side stays in the same place. 0.5f will expand/reduce the window equally to the right and lefts. 1f will do the opposite of 0f
+        * of course.
+        * @return The pack alignment. Always between 0f and 1f, inclusive.
+        * @since 3.5
+        */
+       public final float getPackWidthAlign()
+       {
+               return pwAlign;
+       }
+
+       /** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+        * decides where the extra/superfluous size is placed. 0f means that the window will resize so that the left part moves left and the
+        * right side stays in the same place. 0.5f will expand/reduce the window equally to the right and lefts. 1f will do the opposite of 0f
+        * of course.
+        * @param align The pack alignment. Always between 0f and 1f, inclusive. Values outside this will be truncated.
+        * @since 3.5
+        */
+       public final void setPackWidthAlign(float align)
+       {
+               pwAlign = Math.max(0f, Math.min(1f, align));
+       }
+
+       /** Returns the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+        * sizes that is not <code>null</code> will be returned directly instead of determining the corresponding size through
+        * asking the components in this container.
+        * @return The width for the container that this layout constraint is set for. Not <code>null</code> but
+        * all sizes can be <code>null</code>.
+        * @since 3.5
+        */
+       public final BoundSize getWidth()
+       {
+               return width;
+       }
+
+       /** Sets the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+        * sizes that is not <code>null</code> will be returned directly instead of determining the corresponding size through
+        * asking the components in this container.
+        * @param size The width for the container that this layout constraint is set for. <code>null</code> is translated to
+        * a bound size containing only null sizes.
+        * @since 3.5
+        */
+       public final void setWidth(BoundSize size)
+       {
+               this.width = size != null ? size : BoundSize.NULL_SIZE;
+       }
+
+       /** Returns the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+        * sizes that is not <code>null</code> will be returned directly instead of determining the corresponding size through
+        * asking the components in this container.
+        * @return The height for the container that this layout constraint is set for. Not <code>null</code> but
+        * all sizes can be <code>null</code>.
+        * @since 3.5
+        */
+       public final BoundSize getHeight()
+       {
+               return height;
+       }
+
+       /** Sets the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+        * sizes that is not <code>null</code> will be returned directly instead of determining the corresponding size through
+        * asking the components in this container.
+        * @param size The height for the container that this layout constraint is set for. <code>null</code> is translated to
+        * a bound size containing only null sizes.
+        * @since 3.5
+        */
+       public final void setHeight(BoundSize size)
+       {
+               this.height = size != null ? size : BoundSize.NULL_SIZE;
+       }
+
+       // ************************************************************************
+       // * Builder methods.
+       // ************************************************************************
+
+       /** Short for, and thus same as, <code>.pack("pref", "pref")</code>.
+        * <p>
+        * Same functionality as {@link #setPackHeight(BoundSize)} and {@link #setPackWidth(net.miginfocom.layout.BoundSize)}
+        * only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.5
+        */
+       public final LC pack()
+       {
+               return pack("pref", "pref");
+       }
+
+       /** Sets the pack width and height.
+        * <p>
+        * Same functionality as {@link #setPackHeight(BoundSize)} and {@link #setPackWidth(net.miginfocom.layout.BoundSize)}
+        * only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param width The pack width. May be <code>null</code>.
+        * @param height The pack height. May be <code>null</code>.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.5
+        */
+       public final LC pack(String width, String height)
+       {
+               setPackWidth(width != null ? ConstraintParser.parseBoundSize(width, false, true) : BoundSize.NULL_SIZE);
+               setPackHeight(height != null ? ConstraintParser.parseBoundSize(height, false, false) : BoundSize.NULL_SIZE);
+               return this;
+       }
+
+       /** Sets the pack width and height alignment.
+        * <p>
+        * Same functionality as {@link #setPackHeightAlign(float)} and {@link #setPackWidthAlign(float)}
+        * only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param alignX The pack width alignment. 0.5f is default.
+        * @param alignY The pack height alignment. 0.5f is default.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.5
+        */
+       public final LC packAlign(float alignX, float alignY)
+       {
+               setPackWidthAlign(alignX);
+               setPackHeightAlign(alignY);
+               return this;
+       }
+
+       /** Sets a wrap after the number of columns/rows that is defined in the {@link net.miginfocom.layout.AC}.
+        * <p>
+        * Same functionality as calling {@link #setWrapAfter(int)} with <code>0</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC wrap()
+       {
+               setWrapAfter(0);
+               return this;
+       }
+
+       /** Same functionality as {@link #setWrapAfter(int)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param count After what cell the grid should always auto wrap. If <code>0</code> the number of columns/rows in the
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC wrapAfter(int count)
+       {
+               setWrapAfter(count);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setNoCache(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC noCache()
+       {
+               setNoCache(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFlowX(boolean)} with <code>false</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC flowY()
+       {
+               setFlowX(false);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFlowX(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC flowX()
+       {
+               setFlowX(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFillX(boolean)} with <code>true</code> and {@link #setFillY(boolean)} with <code>true</code> conmbined.T his method returns
+        * <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC fill()
+       {
+               setFillX(true);
+               setFillY(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFillX(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC fillX()
+       {
+               setFillX(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setFillY(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC fillY()
+       {
+               setFillY(true);
+               return this;
+       }
+
+       /** Same functionality as {@link #setLeftToRight(Boolean)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param b <code>true</code> for forcing left-to-right. <code>false</code> for forcing right-to-left.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC leftToRight(boolean b)
+       {
+               setLeftToRight(b ? Boolean.TRUE : Boolean.FALSE);
+               return this;
+       }
+
+       /** Same functionality as setLeftToRight(false) only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final LC rightToLeft()
+       {
+               setLeftToRight(Boolean.FALSE);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setTopToBottom(boolean)} with <code>false</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC bottomToTop()
+       {
+               setTopToBottom(false);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setTopToBottom(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @since 3.7.2
+        */
+       public final LC topToBottom()
+       {
+               setTopToBottom(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setNoGrid(boolean)} with <code>true</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC noGrid()
+       {
+               setNoGrid(true);
+               return this;
+       }
+
+       /** Same functionality as calling {@link #setVisualPadding(boolean)} with <code>false</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC noVisualPadding()
+       {
+               setVisualPadding(false);
+               return this;
+       }
+
+       /** Sets the same inset (expressed as a <code>UnitValue</code>, e.g. "10px" or "20mm") all around.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param allSides The unit value to set for all sides. May be <code>null</code> which means that the default panel insets
+        * for the platform is used.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setInsets(UnitValue[])
+        */
+       public final LC insetsAll(String allSides)
+       {
+               UnitValue insH = ConstraintParser.parseUnitValue(allSides, true);
+               UnitValue insV = ConstraintParser.parseUnitValue(allSides, false);
+               insets = new UnitValue[] {insV, insH, insV, insH}; // No setter to avoid copy again
+               return this;
+       }
+
+       /** Same functionality as <code>setInsets(ConstraintParser.parseInsets(s, true))</code>. This method returns <code>this</code>
+        * for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param s The string to parse. E.g. "10 10 10 10" or "20". If less than 4 groups the last will be used for the missing.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setInsets(UnitValue[])
+        */
+       public final LC insets(String s)
+       {
+               insets = ConstraintParser.parseInsets(s, true);
+               return this;
+       }
+
+       /** Sets the different insets (expressed as a <code>UnitValue</code>s, e.g. "10px" or "20mm") for the corresponding sides.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param top The top inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+        * side for the platform will be used.
+        * @param left The left inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+        * side for the platform will be used.
+        * @param bottom The bottom inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+        * side for the platform will be used.
+        * @param right The right inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+        * side for the platform will be used.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setInsets(UnitValue[])
+        */
+       public final LC insets(String top, String left, String bottom, String right)
+       {
+               insets = new UnitValue[] { // No setter to avoid copy again
+                               ConstraintParser.parseUnitValue(top, false),
+                               ConstraintParser.parseUnitValue(left, true),
+                               ConstraintParser.parseUnitValue(bottom, false),
+                               ConstraintParser.parseUnitValue(right, true)};
+               return this;
+       }
+
+       /** Same functionality as <code>setAlignX(ConstraintParser.parseUnitValueOrAlign(unitValue, true))</code> only this method returns <code>this</code>
+        * for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param align The align keyword or for instance "100px". E.g "left", "right", "leading" or "trailing".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setAlignX(UnitValue)
+        */
+       public final LC alignX(String align)
+       {
+               setAlignX(ConstraintParser.parseUnitValueOrAlign(align, true, null));
+               return this;
+       }
+
+       /** Same functionality as <code>setAlignY(ConstraintParser.parseUnitValueOrAlign(align, false))</code> only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param align The align keyword or for instance "100px". E.g "top" or "bottom".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setAlignY(UnitValue)
+        */
+       public final LC alignY(String align)
+       {
+               setAlignY(ConstraintParser.parseUnitValueOrAlign(align, false, null));
+               return this;
+       }
+
+       /** Sets both the alignX and alignY as the same time.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param ax The align keyword or for instance "100px". E.g "left", "right", "leading" or "trailing".
+        * @param ay The align keyword or for instance "100px". E.g "top" or "bottom".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #alignX(String)
+        * @see #alignY(String)
+        */
+       public final LC align(String ax, String ay)
+       {
+               if (ax != null)
+                       alignX(ax);
+
+               if (ay != null)
+                       alignY(ay);
+
+               return this;
+       }
+
+       /** Same functionality as <code>setGridGapX(ConstraintParser.parseBoundSize(boundsSize, true, true))</code> only this method
+        * returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param boundsSize The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+        * <code>"50:100:200"</code> or <code>"100px"</code>.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setGridGapX(BoundSize)
+        */
+       public final LC gridGapX(String boundsSize)
+       {
+               setGridGapX(ConstraintParser.parseBoundSize(boundsSize, true, true));
+               return this;
+       }
+
+       /** Same functionality as <code>setGridGapY(ConstraintParser.parseBoundSize(boundsSize, true, false))</code> only this method
+        * returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param boundsSize The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+        * <code>"50:100:200"</code> or <code>"100px"</code>.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setGridGapY(BoundSize)
+        */
+       public final LC gridGapY(String boundsSize)
+       {
+               setGridGapY(ConstraintParser.parseBoundSize(boundsSize, true, false));
+               return this;
+       }
+
+       /** Sets both grid gaps at the same time. see {@link #gridGapX(String)} and {@link #gridGapY(String)}.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param gapx The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+        * <code>"50:100:200"</code> or <code>"100px"</code>.
+        * @param gapy The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+        * <code>"50:100:200"</code> or <code>"100px"</code>.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #gridGapX(String)
+        * @see #gridGapY(String)
+        */
+       public final LC gridGap(String gapx, String gapy)
+       {
+               if (gapx != null)
+                       gridGapX(gapx);
+
+               if (gapy != null)
+                       gridGapY(gapy);
+
+               return this;
+       }
+
+       /** Calls {@link #debug(int)} with 300 as an argument.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setDebugMillis(int)
+        */
+       public final LC debug()
+       {
+               setDebugMillis(300);
+               return this;
+       }
+
+       /** Same functionality as {@link #setDebugMillis(int repaintMillis)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param repaintMillis The new debug repaint interval.
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setDebugMillis(int)
+        */
+       public final LC debug(int repaintMillis)
+       {
+               setDebugMillis(repaintMillis);
+               return this;
+       }
+
+       /** Same functionality as {@link #setHideMode(int mode)} only this method returns <code>this</code> for chaining multiple calls.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+        * @param mode The mode:<br>
+        * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+        * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+        * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+        * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        * @see #setHideMode(int)
+        */
+       public final LC hideMode(int mode)
+       {
+               setHideMode(mode);
+               return this;
+       }
+
+       /** The minimum width for the container. The value will override any value that is set on the container itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+        * @param width The width expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC minWidth(String width)
+       {
+               setWidth(LayoutUtil.derive(getWidth(), ConstraintParser.parseUnitValue(width, true), null, null));
+               return this;
+       }
+
+       /** The width for the container as a min and/or preferred and/or maximum width. The value will override any value that is set on
+        * the container itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+        * @param width The width expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC width(String width)
+       {
+               setWidth(ConstraintParser.parseBoundSize(width, false, true));
+               return this;
+       }
+
+       /** The maximum width for the container. The value will override any value that is set on the container itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+        * @param width The width expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC maxWidth(String width)
+       {
+               setWidth(LayoutUtil.derive(getWidth(), null, null, ConstraintParser.parseUnitValue(width, true)));
+               return this;
+       }
+
+       /** The minimum height for the container. The value will override any value that is set on the container itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+        * @param height The height expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC minHeight(String height)
+       {
+               setHeight(LayoutUtil.derive(getHeight(), ConstraintParser.parseUnitValue(height, false), null, null));
+               return this;
+       }
+
+       /** The height for the container as a min and/or preferred and/or maximum height. The value will override any value that is set on
+        * the container itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcontainers.com.
+        * @param height The height expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC height(String height)
+       {
+               setHeight(ConstraintParser.parseBoundSize(height, false, false));
+               return this;
+       }
+
+       /** The maximum height for the container. The value will override any value that is set on the container itself.
+        * <p>
+        * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcontainers.com.
+        * @param height The height expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+        * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+        */
+       public final LC maxHeight(String height)
+       {
+               setHeight(LayoutUtil.derive(getHeight(), null, null, ConstraintParser.parseUnitValue(height, false)));
+               return this;
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       @Override
+       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+
+       @Override
+       public void writeExternal(ObjectOutput out) throws IOException
+       {
+               if (getClass() == LC.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/LayoutCallback.java b/srcjar/net/miginfocom/layout/LayoutCallback.java
new file mode 100644 (file)
index 0000000..f685fc0
--- /dev/null
@@ -0,0 +1,77 @@
+package net.miginfocom.layout;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A class to extend if you want to provide more control over where a component is placed or the size of it.
+ * <p>
+ * Note! Returned arrays from this class will never be altered. This means that caching of arrays in these methods
+ * is OK.
+ */
+public abstract class LayoutCallback
+{
+       /** Returns a position similar to the "pos" the component constraint.
+        * @param comp The component wrapper that holds the actual component (JComponent is Swing and Control in SWT).
+        * <b>Should not be altered.</b>
+        * @return The [x, y, x2, y2] as explained in the documentation for "pos". If <code>null</code>
+        * is returned nothing is done and this is the default.
+        * @see UnitValue
+        * @see net.miginfocom.layout.ConstraintParser#parseUnitValue(String, boolean)
+        */
+       public UnitValue[] getPosition(ComponentWrapper comp)
+       {
+               return null;
+       }
+
+       /** Returns a size similar to the "width" and "height" in the component constraint.
+        * @param comp The component wrapper that holds the actual component (JComponent is Swing and Control in SWT).
+        * <b>Should not be altered.</b>
+        * @return The [width, height] as explained in the documentation for "width" and "height". If <code>null</code>
+        * is returned nothing is done and this is the default.
+        * @see net.miginfocom.layout.BoundSize
+        * @see net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean)
+        */
+       public BoundSize[] getSize(ComponentWrapper comp)
+       {
+               return null;
+       }
+
+       /** A last minute change of the bounds. The bound for the layout cycle has been set and you can correct there
+        * after any set of rules you like.
+        * @param comp The component wrapper that holds the actual component (JComponent is Swing and Control in SWT).
+        */
+       public void correctBounds(ComponentWrapper comp)
+       {
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/LayoutUtil.java b/srcjar/net/miginfocom/layout/LayoutUtil.java
new file mode 100644 (file)
index 0000000..c817827
--- /dev/null
@@ -0,0 +1,597 @@
+package net.miginfocom.layout;
+
+import java.beans.*;
+import java.io.*;
+import java.util.IdentityHashMap;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A utility class that has only static helper methods.
+ */
+public final class LayoutUtil
+{
+       /** A substitute value for a really large value. Integer.MAX_VALUE is not used since that means a lot of defensive code
+        * for potential overflow must exist in many places. This value is large enough for being unreasonable yet it is hard to
+        * overflow.
+        */
+       public static final int INF = (Integer.MAX_VALUE >> 10) - 100; // To reduce likelihood of overflow errors when calculating.
+
+       /** Tag int for a value that in considered "not set". Used as "null" element in int arrays.
+        */
+       static final int NOT_SET = Integer.MIN_VALUE + 12346;   // Magic value...
+
+       // Index for the different sizes
+       public static final int MIN = 0;
+       public static final int PREF = 1;
+       public static final int MAX = 2;
+
+       public static final int HORIZONTAL = 0;
+       public static final int VERTICAL = 1;
+
+       private static volatile WeakHashMap<Object, String> CR_MAP = null;
+       private static volatile WeakHashMap<Object, Boolean> DT_MAP = null;      // The Containers that have design time. Value not used.
+       private static int eSz = 0;
+       private static int globalDebugMillis = 0;
+    public static final boolean HAS_BEANS = hasBeans();
+
+    private static boolean hasBeans()
+    {
+        try {
+            LayoutUtil.class.getClassLoader().loadClass("java.beans.Beans");
+            return true;
+        } catch (Throwable e) {
+            return false;
+        }
+    }
+
+       private LayoutUtil()
+       {
+       }
+
+       /** Returns the current version of MiG Layout.
+        * @return The current version of MiG Layout. E.g. "3.6.3" or "4.0"
+        */
+       public static String getVersion()
+       {
+               return "5.0";
+       }
+
+       /** If global debug should be on or off. If &gt; 0 then debug is turned on for all MigLayout
+        * instances.
+        * @return The current debug milliseconds.
+        * @see LC#setDebugMillis(int)
+        */
+       public static int getGlobalDebugMillis()
+       {
+               return globalDebugMillis;
+       }
+
+       /** If global debug should be on or off. If &gt; 0 then debug is turned on for all MigLayout
+        * instances.
+        * <p>
+        * Note! This is a passive value and will be read by panels when the needed, which is normally
+        * when they repaint/layout.
+        * @param millis The new debug milliseconds. 0 turns of global debug and leaves debug up to every
+        * individual panel.
+        * @see LC#setDebugMillis(int)
+        */
+       public static void setGlobalDebugMillis(int millis)
+       {
+               globalDebugMillis = millis;
+       }
+
+       /** Sets if design time is turned on for a Container in {@link ContainerWrapper}.
+        * @param cw The container to set design time for. <code>null</code> is legal and can be used as
+        * a key to turn on/off design time "in general". Note though that design time "in general" is
+        * always on as long as there is at least one ContainerWrapper with design time.
+        * <p>
+        * <strong>If this method has not ever been called it will default to what
+        * <code>Beans.isDesignTime()</code> returns.</strong> This means that if you call
+        * this method you indicate that you will take responsibility for the design time value.
+        * @param b <code>true</code> means design time on.
+        */
+       public static void setDesignTime(ContainerWrapper cw, boolean b)
+       {
+               if (DT_MAP == null)
+                       DT_MAP = new WeakHashMap<Object, Boolean>();
+
+               DT_MAP.put((cw != null ? cw.getComponent() : null), b);
+       }
+
+       /** Returns if design time is turned on for a Container in {@link ContainerWrapper}.
+        * @param cw The container to set design time for. <code>null</code> is legal will return <code>true</code>
+        * if there is at least one <code>ContainerWrapper</code> (or <code>null</code>) that have design time
+        * turned on.
+        * @return If design time is set for <code>cw</code>.
+        */
+       public static boolean isDesignTime(ContainerWrapper cw)
+       {
+               if (DT_MAP == null)
+                       return HAS_BEANS && Beans.isDesignTime();
+
+               // assume design time "in general" (cw is null) if there is at least one container with design time
+               // (for storing constraints creation strings in method putCCString())
+               if (cw == null && DT_MAP != null && !DT_MAP.isEmpty() )
+                       return true;
+
+               if (cw != null && DT_MAP.containsKey(cw.getComponent()) == false)
+                       cw = null;
+
+               Boolean b = DT_MAP.get(cw != null ? cw.getComponent() : null);
+               return b != null && b;
+       }
+
+       /** The size of an empty row or columns in a grid during design time.
+        * @return The number of pixels. Default is 15.
+        */
+       public static int getDesignTimeEmptySize()
+       {
+               return eSz;
+       }
+
+       /** The size of an empty row or columns in a grid during design time.
+        * @param pixels The number of pixels. Default is 0 (it was 15 prior to v3.7.2, but since that meant different behaviour
+        * under design time by default it was changed to be 0, same as non-design time). IDE vendors can still set it to 15 to
+        * get the old behaviour.
+        */
+       public static void setDesignTimeEmptySize(int pixels)
+       {
+               eSz = pixels;
+       }
+
+       /** Associates <code>con</code> with the creation string <code>s</code>. The <code>con</code> object should
+        * probably have an equals method that compares identities or <code>con</code> objects that .equals() will only
+        * be able to have <b>one</b> creation string.
+        * <p>
+        * If {@link LayoutUtil#isDesignTime(ContainerWrapper)} returns <code>false</code> the method does nothing.
+        * @param con The object. if <code>null</code> the method does nothing.
+        * @param s The creation string. if <code>null</code> the method does nothing.
+        */
+       static void putCCString(Object con, String s)
+       {
+               if (s != null && con != null && isDesignTime(null)) {
+                       if (CR_MAP == null)
+                               CR_MAP = new WeakHashMap<Object, String>(64);
+
+                       CR_MAP.put(con, s);
+               }
+       }
+
+       /** Sets/add the persistence delegates to be used for a class.
+        * @param c The class to set the registered delegate for.
+        * @param del The new delegate or <code>null</code> to erase to old one.
+        */
+       static synchronized void setDelegate(Class<?> c, PersistenceDelegate del)
+       {
+               try {
+                       Introspector.getBeanInfo(c, Introspector.IGNORE_ALL_BEANINFO).getBeanDescriptor().setValue("persistenceDelegate", del);
+               } catch (Exception ignored) {
+               }
+       }
+
+       /** Returns strings set with {@link #putCCString(Object, String)} or <code>null</code> if nothing is associated or
+        * {@link LayoutUtil#isDesignTime(ContainerWrapper)} returns <code>false</code>.
+        * @param con The constrain object.
+        * @return The creation string or <code>null</code> if nothing is registered with the <code>con</code> object.
+        */
+       static String getCCString(Object con)
+       {
+               return CR_MAP != null ? CR_MAP.get(con) : null;
+       }
+
+       static void throwCC()
+       {
+               throw new IllegalStateException("setStoreConstraintData(true) must be set for strings to be saved.");
+       }
+
+       /** Takes a number on min/preferred/max sizes and resize constraints and returns the calculated sizes which sum should add up to <code>bounds</code>. Whether the sum
+        * will actually equal <code>bounds</code> is dependent on the pref/max sizes and resize constraints.
+        * @param sizes [ix],[MIN][PREF][MAX]. Grid.CompWrap.NOT_SET will be treated as N/A or 0. A "[MIN][PREF][MAX]" array with null elements will be interpreted as very flexible (no bounds)
+        * but if the array itself is null it will not get any size.
+        * @param resConstr Elements can be <code>null</code> and the whole array can be <code>null</code>. <code>null</code> means that the size will not be flexible at all.
+        * Can have length less than <code>sizes</code> in which case the last element should be used for the elements missing.
+        * @param defPushWeights If there is no grow weight for a resConstr the corresponding value of this array is used.
+        * These forced resConstr will be grown last though and only if needed to fill to the bounds.
+        * @param startSizeType The initial size to use. E.g. {@link net.miginfocom.layout.LayoutUtil#MIN}.
+        * @param bounds To use for relative sizes.
+        * @return The sizes. Array length will match <code>sizes</code>.
+        */
+       static int[] calculateSerial(int[][] sizes, ResizeConstraint[] resConstr, Float[] defPushWeights, int startSizeType, int bounds)
+       {
+               float[] lengths = new float[sizes.length];      // heights/widths that are set
+               float usedLength = 0.0f;
+
+               // Give all preferred size to start with
+               for (int i = 0; i < sizes.length; i++) {
+                       if (sizes[i] != null) {
+                               float len = sizes[i][startSizeType] != NOT_SET ? sizes[i][startSizeType] : 0;
+                               int newSizeBounded = getBrokenBoundary(len, sizes[i][MIN], sizes[i][MAX]);
+                               if (newSizeBounded != NOT_SET)
+                                       len = newSizeBounded;
+
+                               usedLength += len;
+                               lengths[i] = len;
+                       }
+               }
+
+               int useLengthI = Math.round(usedLength);
+               if (useLengthI != bounds && resConstr != null) {
+                       boolean isGrow = useLengthI < bounds;
+
+                       // Create a Set with the available priorities
+                       TreeSet<Integer> prioList = new TreeSet<Integer>();
+                       for (int i = 0; i < sizes.length; i++) {
+                               ResizeConstraint resC = (ResizeConstraint) getIndexSafe(resConstr, i);
+                               if (resC != null)
+                                       prioList.add(isGrow ? resC.growPrio : resC.shrinkPrio);
+                       }
+                       Integer[] prioIntegers = prioList.toArray(new Integer[prioList.size()]);
+
+                       for (int force = 0; force <= ((isGrow && defPushWeights != null) ? 1 : 0); force++) {    // Run twice if defGrow and the need for growing.
+                               for (int pr = prioIntegers.length - 1; pr >= 0; pr--) {
+                                       int curPrio = prioIntegers[pr];
+
+                                       float totWeight = 0f;
+                                       Float[] resizeWeight = new Float[sizes.length];
+                                       for (int i = 0; i < sizes.length; i++) {
+                                               if (sizes[i] == null)   // if no min/pref/max size at all do not grow or shrink.
+                                                       continue;
+
+                                               ResizeConstraint resC = (ResizeConstraint) getIndexSafe(resConstr, i);
+                                               if (resC != null) {
+                                                       int prio = isGrow ? resC.growPrio : resC.shrinkPrio;
+
+                                                       if (curPrio == prio) {
+                                                               if (isGrow) {
+                                                                       resizeWeight[i] = (force == 0 || resC.grow != null) ? resC.grow : (defPushWeights[i < defPushWeights.length ? i : defPushWeights.length - 1]);
+                                                               } else {
+                                                                       resizeWeight[i] = resC.shrink;
+                                                               }
+                                                               if (resizeWeight[i] != null)
+                                                                       totWeight += resizeWeight[i];
+                                                       }
+                                               }
+                                       }
+
+                                       if (totWeight > 0f) {
+                                               boolean hit;
+                                               do {
+                                                       float toChange = bounds - usedLength;
+                                                       hit = false;
+                                                       float changedWeight = 0f;
+                                                       for (int i = 0; i < sizes.length && totWeight > 0.0001f; i++) {
+
+                                                               Float weight = resizeWeight[i];
+                                                               if (weight != null) {
+                                                                       float sizeDelta = toChange * weight / totWeight;
+                                                                       float newSize = lengths[i] + sizeDelta;
+
+                                                                       if (sizes[i] != null) {
+                                                                               int newSizeBounded = getBrokenBoundary(newSize, sizes[i][MIN], sizes[i][MAX]);
+                                                                               if (newSizeBounded != NOT_SET) {
+                                                                                       resizeWeight[i] = null;
+                                                                                       hit = true;
+                                                                                       changedWeight += weight;
+                                                                                       newSize = newSizeBounded;
+                                                                                       sizeDelta = newSize - lengths[i];
+                                                                               }
+                                                                       }
+
+                                                                       lengths[i] = newSize;
+                                                                       usedLength += sizeDelta;
+                                                               }
+                                                       }
+                                                       totWeight -= changedWeight;
+                                               } while (hit);
+                                       }
+                               }
+                       }
+               }
+               return roundSizes(lengths);
+       }
+
+       static Object getIndexSafe(Object[] arr, int ix)
+       {
+               return arr != null ? arr[ix < arr.length ? ix : arr.length - 1] : null;
+       }
+
+       /** Returns the broken boundary if <code>sz</code> is outside the boundaries <code>lower</code> or <code>upper</code>. If both boundaries
+        * are broken, the lower one is returned. If <code>sz</code> is &lt; 0 then <code>new Float(0f)</code> is returned so that no sizes can be
+        * negative.
+        * @param sz The size to check
+        * @param lower The lower boundary (or <code>null</code> for no boundary).
+        * @param upper The upper boundary (or <code>null</code> for no boundary).
+        * @return The broken boundary.
+        */
+       private static int getBrokenBoundary(float sz, int lower, int upper)
+       {
+               if (lower != NOT_SET) {
+                       if (sz < lower)
+                               return lower;
+               } else if (sz < 0f) {
+                       return 0;
+               }
+
+               if (upper != NOT_SET && sz > upper)
+                       return upper;
+
+               return NOT_SET;
+       }
+
+
+       static int sum(int[] terms, int start, int len)
+       {
+               int s = 0;
+               for (int i = start, iSz = start + len; i < iSz; i++)
+                       s += terms[i];
+               return s;
+       }
+
+       static int sum(int[] terms)
+       {
+               return sum(terms, 0, terms.length);
+       }
+
+       /** Keeps f within min and max. Min is of higher priority if min is larger than max.
+        * @param f The value to clamp
+        * @param min
+        * @param max
+        * @return The clamped value, between min and max.
+        */
+       static float clamp(float f, float min, float max)
+       {
+               return Math.max(min, Math.min(f, max));
+       }
+
+       /** Keeps i within min and max. Min is of higher priority if min is larger than max.
+        * @param i The value to clamp
+        * @param min
+        * @param max
+        * @return The clamped value, between min and max.
+        */
+       static int clamp(int i, int min, int max)
+       {
+               return Math.max(min, Math.min(i, max));
+       }
+
+       public static int getSizeSafe(int[] sizes, int sizeType)
+       {
+               if (sizes == null || sizes[sizeType] == NOT_SET)
+                       return sizeType == MAX ? LayoutUtil.INF : 0;
+               return sizes[sizeType];
+       }
+
+       static BoundSize derive(BoundSize bs, UnitValue min, UnitValue pref, UnitValue max)
+       {
+               if (bs == null || bs.isUnset())
+                       return new BoundSize(min, pref, max, null);
+
+               return new BoundSize(
+                               min != null ? min : bs.getMin(),
+                               pref != null ? pref : bs.getPreferred(),
+                               max != null ? max : bs.getMax(),
+                               bs.getGapPush(),
+                               null);
+       }
+
+       /** Returns if left-to-right orientation is used. If not set explicitly in the layout constraints the Locale
+        * of the <code>parent</code> is used.
+        * @param lc The constraint if there is one. Can be <code>null</code>.
+        * @param container The parent that may be used to get the left-to-right if lc does not specify this.
+        * @return If left-to-right orientation is currently used.
+        */
+       public static boolean isLeftToRight(LC lc, ContainerWrapper container)
+       {
+               if (lc != null && lc.getLeftToRight() != null)
+                       return lc.getLeftToRight();
+
+               return container == null || container.isLeftToRight();
+       }
+
+       /** Round a number of float sizes into int sizes so that the total length match up
+        * @param sizes The sizes to round
+        * @return An array of equal length as <code>sizes</code>.
+        */
+       static int[] roundSizes(float[] sizes)
+       {
+               int[] retInts = new int[sizes.length];
+               float posD = 0;
+
+               for (int i = 0; i < retInts.length; i++) {
+                       int posI = (int) (posD + 0.5f);
+
+                       posD += sizes[i];
+
+                       retInts[i] = (int) (posD + 0.5f) - posI;
+               }
+
+               return retInts;
+       }
+
+       /** Safe equals. null == null, but null never equals anything else.
+        * @param o1 The first object. May be <code>null</code>.
+        * @param o2 The second object. May be <code>null</code>.
+        * @return Returns <code>true</code> if <code>o1</code> and <code>o2</code> are equal (using .equals()) or both are <code>null</code>.
+        */
+       static boolean equals(Object o1, Object o2)
+       {
+               return o1 == o2 || (o1 != null && o2 != null && o1.equals(o2));
+       }
+
+//     static int getBaselineCorrect(Component comp)
+//     {
+//             Dimension pSize = comp.getPreferredSize();
+//             int baseline = comp.getBaseline(pSize.width, pSize.height);
+//             int nextBaseline = comp.getBaseline(pSize.width, pSize.height + 1);
+//
+//             // Amount to add to height when calculating where baseline
+//             // lands for a particular height:
+//             int padding = 0;
+//
+//             // Where the baseline is relative to the mid point
+//             int baselineOffset = baseline - pSize.height / 2;
+//             if (pSize.height % 2 == 0 && baseline != nextBaseline) {
+//                     padding = 1;
+//             } else if (pSize.height % 2 == 1 && baseline == nextBaseline) {
+//                     baselineOffset--;
+//                     padding = 1;
+//             }
+//
+//             // The following calculates where the baseline lands for
+//             // the height z:
+//             return (pSize.height + padding) / 2 + baselineOffset;
+//     }
+
+
+       /** Returns the insets for the side.
+        * @param side top == 0, left == 1, bottom = 2, right = 3.
+        * @param getDefault If <code>true</code> the default insets will get retrieved if <code>lc</code> has none set.
+        * @return The insets for the side. Never <code>null</code>.
+        */
+       static UnitValue getInsets(LC lc, int side, boolean getDefault)
+       {
+               UnitValue[] i = lc.getInsets();
+               return (i != null && i[side] != null) ? i[side] : (getDefault ? PlatformDefaults.getPanelInsets(side) : UnitValue.ZERO);
+       }
+
+       /** Writes the object and CLOSES the stream. Uses the persistence delegate registered in this class.
+        * @param os The stream to write to. Will be closed.
+        * @param o The object to be serialized.
+        * @param listener The listener to receive the exceptions if there are any. If <code>null</code> not used.
+        */
+       static void writeXMLObject(OutputStream os, Object o, ExceptionListener listener)
+       {
+               ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+               Thread.currentThread().setContextClassLoader(LayoutUtil.class.getClassLoader());
+
+               XMLEncoder encoder = new XMLEncoder(os);
+
+               if (listener != null)
+                       encoder.setExceptionListener(listener);
+
+               encoder.writeObject(o);
+        encoder.close();    // Must be closed to write.
+
+               Thread.currentThread().setContextClassLoader(oldClassLoader);
+       }
+
+       private static ByteArrayOutputStream writeOutputStream = null;
+       /** Writes an object to XML.
+        * @param out The object out to write to. Will not be closed.
+        * @param o The object to write.
+        */
+       public static synchronized void writeAsXML(ObjectOutput out, Object o) throws IOException
+       {
+               if (writeOutputStream == null)
+                       writeOutputStream = new ByteArrayOutputStream(16384);
+
+               writeOutputStream.reset();
+
+               writeXMLObject(writeOutputStream, o, new ExceptionListener() {
+                       @Override
+                       public void exceptionThrown(Exception e) {
+                               e.printStackTrace();
+                       }});
+
+               byte[] buf = writeOutputStream.toByteArray();
+
+               out.writeInt(buf.length);
+               out.write(buf);
+       }
+
+       private static byte[] readBuf = null;
+       /** Reads an object from <code>in</code> using the
+        * @param in The object input to read from.
+        * @return The object. Never <code>null</code>.
+        * @throws IOException If there was a problem saving as XML
+        */
+       public static synchronized Object readAsXML(ObjectInput in) throws IOException
+       {
+               if (readBuf == null)
+                       readBuf = new byte[16384];
+
+               Thread cThread = Thread.currentThread();
+               ClassLoader oldCL = null;
+
+               try {
+                       oldCL = cThread.getContextClassLoader();
+                       cThread.setContextClassLoader(LayoutUtil.class.getClassLoader());
+               } catch(SecurityException ignored) {
+               }
+
+               Object o = null;
+               try {
+                       int length = in.readInt();
+                       if (length > readBuf.length)
+                               readBuf = new byte[length];
+
+                       in.readFully(readBuf, 0, length);
+
+                       o = new XMLDecoder(new ByteArrayInputStream(readBuf, 0, length)).readObject();
+
+               } catch(EOFException ignored) {
+               }
+
+               if (oldCL != null)
+                       cThread.setContextClassLoader(oldCL);
+
+               return o;
+       }
+
+       private static final IdentityHashMap<Object, Object> SER_MAP = new IdentityHashMap<Object, Object>(2);
+
+       /** Sets the serialized object and associates it with <code>caller</code>.
+        * @param caller The object created <code>o</code>
+        * @param o The just serialized object.
+        */
+       public static void setSerializedObject(Object caller, Object o)
+       {
+               synchronized(SER_MAP) {
+                       SER_MAP.put(caller, o);
+               }
+       }
+
+       /** Returns the serialized object that are associated with <code>caller</code>. It also removes it from the list.
+        * @param caller The original creator of the object.
+        * @return The object.
+        */
+       public static Object getSerializedObject(Object caller)
+       {
+               synchronized(SER_MAP) {
+                       return SER_MAP.remove(caller);
+               }
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/LinkHandler.java b/srcjar/net/miginfocom/layout/LinkHandler.java
new file mode 100644 (file)
index 0000000..2aab6f1
--- /dev/null
@@ -0,0 +1,182 @@
+package net.miginfocom.layout;
+
+import java.util.HashMap;
+import java.util.WeakHashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/**
+ */
+public final class LinkHandler
+{
+       public static final int X = 0;
+       public static final int Y = 1;
+       public static final int WIDTH = 2;
+       public static final int HEIGHT = 3;
+       public static final int X2 = 4;
+       public static final int Y2 = 5;
+
+       // indices for values of LAYOUTS
+       private static final int VALUES = 0;
+       private static final int VALUES_TEMP = 1;
+
+       private static final WeakHashMap<Object, HashMap<String, int[]>[]> LAYOUTS = new WeakHashMap<Object, HashMap<String, int[]>[]>();
+
+       private LinkHandler()
+       {
+       }
+
+       public synchronized static Integer getValue(Object layout, String key, int type)
+       {
+               Integer ret = null;
+
+               HashMap<String, int[]>[] layoutValues = LAYOUTS.get(layout);
+               if (layoutValues != null) {
+                       int[] rect = layoutValues[VALUES_TEMP].get(key);
+                       if (rect != null && rect[type] != LayoutUtil.NOT_SET) {
+                               ret = rect[type];
+                       } else {
+                               rect = layoutValues[VALUES].get(key);
+                               ret = (rect != null && rect[type] != LayoutUtil.NOT_SET) ? rect[type] : null;
+                       }
+               }
+               return ret;
+       }
+
+       /** Sets a key that can be linked to from any component.
+        * @param layout The MigLayout instance
+        * @param key The key to link to. This is the same as the ID in a component constraint.
+        * @param x x
+        * @param y y
+        * @param width Width
+        * @param height Height
+        * @return If the value was changed
+        */
+       public synchronized static boolean setBounds(Object layout, String key, int x, int y, int width, int height)
+       {
+               return setBounds(layout, key, x, y, width, height, false, false);
+       }
+
+       synchronized static boolean setBounds(Object layout, String key, int x, int y, int width, int height, boolean temporary, boolean incCur)
+       {
+               HashMap<String, int[]>[] layoutValues = LAYOUTS.get(layout);
+               if (layoutValues != null) {
+                       HashMap<String, int[]> map = layoutValues[temporary ? VALUES_TEMP : VALUES];
+                       int[] old = map.get(key);
+
+                       if (old == null || old[X] != x || old[Y] != y || old[WIDTH] != width || old[HEIGHT] != height) {
+                               if (old == null || incCur == false) {
+                                       map.put(key, new int[] {x, y, width, height, x + width, y + height});
+                                       return true;
+                               } else {
+                                       boolean changed = false;
+
+                                       if (x != LayoutUtil.NOT_SET) {
+                                               if (old[X] == LayoutUtil.NOT_SET || x < old[X]) {
+                                                       old[X] = x;
+                                                       old[WIDTH] = old[X2] - x;
+                                                       changed = true;
+                                               }
+
+                                               if (width != LayoutUtil.NOT_SET) {
+                                                       int x2 = x + width;
+                                                       if (old[X2] == LayoutUtil.NOT_SET || x2 > old[X2]) {
+                                                               old[X2] = x2;
+                                                               old[WIDTH] = x2 - old[X];
+                                                               changed = true;
+                                                       }
+                                               }
+                                       }
+
+                                       if (y != LayoutUtil.NOT_SET) {
+                                               if (old[Y] == LayoutUtil.NOT_SET || y < old[Y]) {
+                                                       old[Y] = y;
+                                                       old[HEIGHT] = old[Y2] - y;
+                                                       changed = true;
+                                               }
+
+                                               if (height != LayoutUtil.NOT_SET) {
+                                                       int y2 = y + height;
+                                                       if (old[Y2] == LayoutUtil.NOT_SET || y2 > old[Y2]) {
+                                                               old[Y2] = y2;
+                                                               old[HEIGHT] = y2 - old[Y];
+                                                               changed = true;
+                                                       }
+                                               }
+                                       }
+                                       return changed;
+                               }
+                       }
+                       return false;
+               }
+
+               int[] bounds = new int[] {x, y, width, height, x + width, y + height};
+
+               HashMap<String, int[]> values_temp = new HashMap<String, int[]>(4);
+               if (temporary)
+                       values_temp.put(key, bounds);
+
+               HashMap<String, int[]> values = new HashMap<String, int[]>(4);
+               if (temporary == false)
+                       values.put(key, bounds);
+
+               LAYOUTS.put(layout, new HashMap[] {values, values_temp});
+
+               return true;
+       }
+
+       /** This method clear any weak references right away instead of waiting for the GC. This might be advantageous
+        * if lots of layout are created and disposed of quickly to keep memory consumption down.
+        * @since 3.7.4
+        */
+       public synchronized static void clearWeakReferencesNow()
+       {
+               LAYOUTS.clear();
+       }
+
+       public synchronized static boolean clearBounds(Object layout, String key)
+       {
+               HashMap<String, int[]>[] layoutValues = LAYOUTS.get(layout);
+               if (layoutValues != null)
+                       return layoutValues[VALUES].remove(key) != null;
+               return false;
+       }
+
+       synchronized static void clearTemporaryBounds(Object layout)
+       {
+               HashMap<String, int[]>[] layoutValues = LAYOUTS.get(layout);
+               if (layoutValues != null)
+                       layoutValues[VALUES_TEMP].clear();
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/PlatformDefaults.java b/srcjar/net/miginfocom/layout/PlatformDefaults.java
new file mode 100644 (file)
index 0000000..e1b72c4
--- /dev/null
@@ -0,0 +1,855 @@
+package net.miginfocom.layout;
+
+import java.util.HashMap;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ * @author Xxxx Xxxx, Xxxx  - Gnome support
+ *         Date: 2008-jan-16
+ */
+
+/** Currently handles Windows, Mac OS X, and GNOME spacing.
+ */
+public final class PlatformDefaults
+{
+       /** Property to use in LAF settings and as JComponent client property
+        * to specify the visual padding.
+        * <p>
+        */
+       public static String VISUAL_PADDING_PROPERTY = "visualPadding";
+
+       private static int DEF_H_UNIT = UnitValue.LPX;
+       private static int DEF_V_UNIT = UnitValue.LPY;
+
+       private static InCellGapProvider GAP_PROVIDER = null;
+
+       private static volatile int MOD_COUNT = 0;
+
+//     private static final UnitValue LPX1 = new UnitValue(1, UnitValue.LPX, null);
+//     private static final UnitValue LPX4 = new UnitValue(4, UnitValue.LPX, null);
+       private static final UnitValue LPX6 = new UnitValue(6, UnitValue.LPX, null);
+       private static final UnitValue LPX7 = new UnitValue(7, UnitValue.LPX, null);
+//     private static final UnitValue LPX8 = new UnitValue(8, UnitValue.LPX, null);
+//     private static final UnitValue LPX9 = new UnitValue(9, UnitValue.LPX, null);
+//     private static final UnitValue LPX10 = new UnitValue(10, UnitValue.LPX, null);
+       private static final UnitValue LPX11 = new UnitValue(11, UnitValue.LPX, null);
+       private static final UnitValue LPX12 = new UnitValue(12, UnitValue.LPX, null);
+//     private static final UnitValue LPX14 = new UnitValue(14, UnitValue.LPX, null);
+       private static final UnitValue LPX16 = new UnitValue(16, UnitValue.LPX, null);
+       private static final UnitValue LPX18 = new UnitValue(18, UnitValue.LPX, null);
+       private static final UnitValue LPX20 = new UnitValue(20, UnitValue.LPX, null);
+
+//     private static final UnitValue LPY1 = new UnitValue(1, UnitValue.LPY, null);
+//     private static final UnitValue LPY4 = new UnitValue(4, UnitValue.LPY, null);
+       private static final UnitValue LPY6 = new UnitValue(6, UnitValue.LPY, null);
+       private static final UnitValue LPY7 = new UnitValue(7, UnitValue.LPY, null);
+//     private static final UnitValue LPY8 = new UnitValue(8, UnitValue.LPY, null);
+//     private static final UnitValue LPY9 = new UnitValue(9, UnitValue.LPY, null);
+//     private static final UnitValue LPY10 = new UnitValue(10, UnitValue.LPY, null);
+       private static final UnitValue LPY11 = new UnitValue(11, UnitValue.LPY, null);
+       private static final UnitValue LPY12 = new UnitValue(12, UnitValue.LPY, null);
+//     private static final UnitValue LPY14 = new UnitValue(14, UnitValue.LPY, null);
+       private static final UnitValue LPY16 = new UnitValue(16, UnitValue.LPY, null);
+       private static final UnitValue LPY18 = new UnitValue(18, UnitValue.LPY, null);
+       private static final UnitValue LPY20 = new UnitValue(20, UnitValue.LPY, null);
+
+       public static final int WINDOWS_XP = 0;
+       public static final int MAC_OSX = 1;
+       public static final int GNOME = 2;
+//     private static final int KDE = 3;
+
+       private static int CUR_PLAF = WINDOWS_XP;
+
+       // Used for holding values.
+       private final static UnitValue[] PANEL_INS = new UnitValue[4];
+       private final static UnitValue[] DIALOG_INS = new UnitValue[4];
+
+       private static String BUTTON_FORMAT = null;
+
+       private static final HashMap<String, UnitValue> HOR_DEFS = new HashMap<String, UnitValue>(32);
+       private static final HashMap<String, UnitValue> VER_DEFS = new HashMap<String, UnitValue>(32);
+       private static BoundSize DEF_VGAP = null, DEF_HGAP = null;
+       static BoundSize RELATED_X = null, RELATED_Y = null, UNRELATED_X = null, UNRELATED_Y = null;
+       private static UnitValue BUTT_WIDTH = null;
+       private static UnitValue BUTT_PADDING = null;
+
+       private static Float horScale = null, verScale = null;
+
+       /** I value indicating that the size of the font for the container of the component
+        * will be used as a base for calculating the logical pixel size. This is much as how
+        * Windows calculated DLU (dialog units).
+        * @see net.miginfocom.layout.UnitValue#LPX
+        * @see net.miginfocom.layout.UnitValue#LPY
+        * @see #setLogicalPixelBase(int)
+        */
+       public static final int BASE_FONT_SIZE = 100;
+
+       /** I value indicating that the screen DPI will be used as a base for calculating the
+        * logical pixel size.
+        * <p>
+        * This is the default value.
+        * @see net.miginfocom.layout.UnitValue#LPX
+        * @see net.miginfocom.layout.UnitValue#LPY
+        * @see #setLogicalPixelBase(int)
+        * @see #setVerticalScaleFactor(Float)
+        * @see #setHorizontalScaleFactor(Float)
+        */
+       public static final int BASE_SCALE_FACTOR = 101;
+
+       /** I value indicating that the size of a logical pixel should always be a real pixel
+        * and thus no compensation will be made.
+        * @see net.miginfocom.layout.UnitValue#LPX
+        * @see net.miginfocom.layout.UnitValue#LPY
+        * @see #setLogicalPixelBase(int)
+        */
+       public static final int BASE_REAL_PIXEL = 102;
+
+       private static int LP_BASE = BASE_SCALE_FACTOR;
+
+       private static Integer BASE_DPI_FORCED = null;
+       private static int BASE_DPI = 96;
+
+       private static boolean dra = true;
+
+       private static final HashMap<String, int[]> VISUAL_BOUNDS = new HashMap<String, int[]>(64);
+
+       static {
+               setPlatform(getCurrentPlatform());
+               MOD_COUNT = 0;
+       }
+
+       /** Returns the platform that the JRE is running on currently.
+        * @return The platform that the JRE is running on currently. E.g. {@link #MAC_OSX}, {@link #WINDOWS_XP}, or {@link #GNOME}.
+        */
+       public static int getCurrentPlatform()
+       {
+               final String os = System.getProperty("os.name");
+               if (os.startsWith("Mac OS")) {
+                       return MAC_OSX;
+               } else if (os.startsWith("Linux")) {
+                       return GNOME;
+               } else {
+                       return WINDOWS_XP;
+               }
+       }
+
+       private PlatformDefaults()
+       {
+       }
+
+       /** Set the defaults to the default for the platform
+        * @param plaf The platform. <code>PlatformDefaults.WINDOWS_XP</code>,
+        * <code>PlatformDefaults.MAC_OSX</code>, or
+        * <code>PlatformDefaults.GNOME</code>.
+        */
+       public static void setPlatform(int plaf)
+       {
+               switch (plaf) {
+                       case WINDOWS_XP:
+                               setDefaultVisualPadding("TabbedPane." + VISUAL_PADDING_PROPERTY, new int[]{1, 0, 1, 2});
+                               setRelatedGap(LPX7, LPY7);
+                               setUnrelatedGap(LPX11, LPY11);
+                               setParagraphGap(LPX20, LPY20);
+                               setIndentGap(LPX11, LPY11);
+                               setGridCellGap(LPX7, LPY7);
+
+                               setMinimumButtonWidth(new UnitValue(75, UnitValue.LPX, null));
+                               setButtonOrder("L_E+U+YNBXOCAH_I_R");
+                               setDialogInsets(LPY11, LPX11, LPY11, LPX11);
+                               setPanelInsets(LPY7, LPX7, LPY7, LPX7);
+                               break;
+
+                       case MAC_OSX:
+
+                               setDefaultVisualPadding("Button." + VISUAL_PADDING_PROPERTY, new int[]{3, 6, 5, 6});
+                               setDefaultVisualPadding("Button.icon." + VISUAL_PADDING_PROPERTY, new int[]{3, 2, 3, 2});
+                               setDefaultVisualPadding("Button.square." + VISUAL_PADDING_PROPERTY, new int[]{4, 4, 4, 4});
+                               setDefaultVisualPadding("Button.square.icon." + VISUAL_PADDING_PROPERTY, new int[]{4, 4, 4, 4});
+                               setDefaultVisualPadding("Button.gradient." + VISUAL_PADDING_PROPERTY, new int[]{5, 4, 5, 4});
+                               setDefaultVisualPadding("Button.gradient.icon." + VISUAL_PADDING_PROPERTY, new int[]{5, 4, 5, 4});
+                               setDefaultVisualPadding("Button.bevel." + VISUAL_PADDING_PROPERTY, new int[]{2, 2, 3, 2});
+                               setDefaultVisualPadding("Button.bevel.icon." + VISUAL_PADDING_PROPERTY, new int[]{2, 2, 3, 2});
+                               setDefaultVisualPadding("Button.textured." + VISUAL_PADDING_PROPERTY, new int[]{3, 2, 3, 2});
+                               setDefaultVisualPadding("Button.textured.icon." + VISUAL_PADDING_PROPERTY, new int[]{3, 2, 3, 2});
+                               setDefaultVisualPadding("Button.roundRect." + VISUAL_PADDING_PROPERTY, new int[]{5, 4, 5, 4});
+                               setDefaultVisualPadding("Button.roundRect.icon." + VISUAL_PADDING_PROPERTY, new int[]{5, 4, 5, 4});
+                               setDefaultVisualPadding("Button.recessed." + VISUAL_PADDING_PROPERTY, new int[]{5, 4, 5, 4});
+                               setDefaultVisualPadding("Button.recessed.icon." + VISUAL_PADDING_PROPERTY, new int[]{5, 4, 5, 4});
+                               setDefaultVisualPadding("Button.help." + VISUAL_PADDING_PROPERTY, new int[]{4, 3, 3, 4});
+                               setDefaultVisualPadding("Button.help.icon." + VISUAL_PADDING_PROPERTY, new int[]{4, 3, 3, 4});
+
+                               setDefaultVisualPadding("ComboBox." + VISUAL_PADDING_PROPERTY, new int[]{2, 4, 4, 5});
+                               setDefaultVisualPadding("ComboBox.isPopDown." + VISUAL_PADDING_PROPERTY, new int[]{2, 5, 4, 5});
+                               setDefaultVisualPadding("ComboBox.isSquare." + VISUAL_PADDING_PROPERTY, new int[]{1, 6, 5, 7});
+
+                               setDefaultVisualPadding("ComboBox.editable." + VISUAL_PADDING_PROPERTY, new int[]{3, 3, 3, 2});
+                               setDefaultVisualPadding("ComboBox.editable.isSquare." + VISUAL_PADDING_PROPERTY, new int[]{3, 3, 3, 1});
+
+                               setDefaultVisualPadding("TextField." + VISUAL_PADDING_PROPERTY, new int[]{3, 3, 3, 3});
+                               setDefaultVisualPadding("TabbedPane." + VISUAL_PADDING_PROPERTY, new int[]{4, 8, 11, 8});
+
+                               setDefaultVisualPadding("Spinner." + VISUAL_PADDING_PROPERTY, new int[]{3, 3, 3, 1});
+
+                               setDefaultVisualPadding("RadioButton." + VISUAL_PADDING_PROPERTY, new int[]{4, 6, 3, 5});
+                               setDefaultVisualPadding("RadioButton.small." + VISUAL_PADDING_PROPERTY, new int[]{4, 6, 3, 5});
+                               setDefaultVisualPadding("RadioButton.mini." + VISUAL_PADDING_PROPERTY, new int[]{5, 7, 4, 5});
+                               setDefaultVisualPadding("CheckBox." + VISUAL_PADDING_PROPERTY, new int[]{5, 7, 4, 5});
+                               setDefaultVisualPadding("CheckBox.small." + VISUAL_PADDING_PROPERTY, new int[]{5, 7, 4, 5});
+                               setDefaultVisualPadding("CheckBox.mini." + VISUAL_PADDING_PROPERTY, new int[]{6, 7, 3, 5});
+
+                               setRelatedGap(LPX7, LPY7);
+                               setUnrelatedGap(LPX11, LPY11);
+                               setParagraphGap(LPX20, LPY20);
+                               setIndentGap(LPX11, LPY11);
+                               setGridCellGap(LPX7, LPY7);
+
+                               setMinimumButtonWidth(new UnitValue(70, UnitValue.LPX, null));
+                               setMinimumButtonPadding(new UnitValue(8, UnitValue.LPX, null));
+                               setButtonOrder("L_HE+U+NYBXCOA_I_R");
+                               setDialogInsets(LPY20, LPX20, LPY20, LPX20);
+                               setPanelInsets(LPY16, LPX16, LPY16, LPX16);
+                               break;
+
+                       case GNOME:
+                               setRelatedGap(LPX6, LPY6);                    // GNOME HIG 8.2.3
+                               setUnrelatedGap(LPX12, LPY12);                // GNOME HIG 8.2.3
+                               setParagraphGap(LPX18, LPY18);                // GNOME HIG 8.2.3
+                               setIndentGap(LPX12, LPY12);                   // GNOME HIG 8.2.3
+                               setGridCellGap(LPX6, LPY6);                   // GNOME HIG 8.2.3
+
+                               // GtkButtonBox, child-min-width property default value
+                               setMinimumButtonWidth(new UnitValue(85, UnitValue.LPX, null));
+                               setButtonOrder("L_HE+UNYACBXO_I_R");          // GNOME HIG 3.4.2, 3.7.1
+                               setDialogInsets(LPY12, LPX12, LPY12, LPX12);  // GNOME HIG 3.4.3
+                               setPanelInsets(LPY6, LPX6, LPY6, LPX6);       // ???
+                               break;
+                       default:
+                               throw new IllegalArgumentException("Unknown platform: " + plaf);
+               }
+               CUR_PLAF = plaf;
+               BASE_DPI = BASE_DPI_FORCED != null ? BASE_DPI_FORCED : getPlatformDPI(plaf);
+       }
+
+       /** Sets the visual bounds for a component type.
+        * @param key The component type. E.g. "TabbedPane.visualPadding" or "ComboBox.editable.isSquare.visualPadding". See source code for list.
+        * @param insets Top, left, bottom, right. Always length 4 or null.
+        * @see net.miginfocom.layout.ComponentWrapper#getVisualPadding()
+        */
+       public static void setDefaultVisualPadding(String key, int[] insets)
+       {
+               VISUAL_BOUNDS.put(key, insets);
+       }
+
+       /** Returns the visual bounds for a component type.
+        * @param key The component type. E.g. "TabbedPane.visualPadding" or "ComboBox.editable.isSquare.visualPadding". See source code for list.
+        * @return insets Top, left, bottom, right. Always length 4 or null. Live object, MUST NOT BE CHANGED!.
+        * @see net.miginfocom.layout.ComponentWrapper#getVisualPadding()
+        */
+       public static int[] getDefaultVisualPadding(String key)
+       {
+               return VISUAL_BOUNDS.get(key);
+       }
+
+       public static int getPlatformDPI(int plaf)
+       {
+               switch (plaf) {
+                       case WINDOWS_XP:
+                       case GNOME:
+                               return 96;
+                       case MAC_OSX:
+                               try {
+                                       return java.awt.Toolkit.getDefaultToolkit().getScreenResolution();
+                               } catch (Throwable t) {
+                                       return 72;
+                               }
+                       default:
+                               throw new IllegalArgumentException("Unknown platform: " + plaf);
+               }
+       }
+
+       /** Returns the current platform
+        * @return <code>PlatformDefaults.WINDOWS</code> or <code>PlatformDefaults.MAC_OSX</code>
+        */
+       public static int getPlatform()
+       {
+               return CUR_PLAF;
+       }
+
+       public static int getDefaultDPI()
+       {
+               return BASE_DPI;
+       }
+
+       /** Sets the default platform DPI. Normally this is set in the {@link #setPlatform(int)} for the different platforms
+        * but it can be tweaked here. For instance SWT on Mac does this.
+        * <p>
+        * Note that this is not the actual current DPI, but the base DPI for the toolkit.
+        * @param dpi The base DPI. If null the default DPI is reset to the platform base DPI.
+        */
+       public static void setDefaultDPI(Integer dpi)
+       {
+               BASE_DPI = dpi != null ? dpi : getPlatformDPI(CUR_PLAF);
+               BASE_DPI_FORCED = dpi;
+       }
+
+       /** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+        * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+        * (72 DPI for Mac and 92 DPI for Windows).
+        * @return The forced scale or <code>null</code> for default scaling.
+        * @see #getHorizontalScaleFactor()
+        * @see ComponentWrapper#getHorizontalScreenDPI()
+        */
+       public static Float getHorizontalScaleFactor()
+       {
+               return horScale;
+       }
+
+       /** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+        * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+        * (72 DPI for Mac and 92 DPI for Windows).
+        * @param f The forced scale or <code>null</code> for default scaling.
+        * @see #getHorizontalScaleFactor()
+        * @see ComponentWrapper#getHorizontalScreenDPI()
+        */
+       public static void setHorizontalScaleFactor(Float f)
+       {
+               if (!LayoutUtil.equals(horScale, f)) {
+                       horScale = f;
+                       MOD_COUNT++;
+               }
+       }
+
+       /** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+        * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+        * (72 DPI for Mac and 92 DPI for Windows).
+        * @return The forced scale or <code>null</code> for default scaling.
+        * @see #getHorizontalScaleFactor()
+        * @see ComponentWrapper#getVerticalScreenDPI()
+        */
+       public static Float getVerticalScaleFactor()
+       {
+               return verScale;
+       }
+
+       /** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+        * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+        * (72 DPI for Mac and 92 DPI for Windows).
+        * @param f The forced scale or <code>null</code> for default scaling.
+        * @see #getHorizontalScaleFactor()
+        * @see ComponentWrapper#getVerticalScreenDPI()
+        */
+       public static void setVerticalScaleFactor(Float f)
+       {
+               if (!LayoutUtil.equals(verScale, f)) {
+                       verScale = f;
+                       MOD_COUNT++;
+               }
+       }
+
+       /** What base value should be used to calculate logical pixel sizes.
+        * @return The current base. Default is {@link #BASE_SCALE_FACTOR}
+        * @see #BASE_FONT_SIZE
+        * @see #BASE_SCALE_FACTOR
+        * @see #BASE_REAL_PIXEL
+*/
+       public static int getLogicalPixelBase()
+       {
+               return LP_BASE;
+       }
+
+       /** What base value should be used to calculate logical pixel sizes.
+        * @param base The new base. Default is {@link #BASE_SCALE_FACTOR}
+        * @see #BASE_FONT_SIZE
+        * @see #BASE_SCALE_FACTOR
+        * @see #BASE_REAL_PIXEL
+        */
+       public static void setLogicalPixelBase(int base)
+       {
+               if (LP_BASE != base) {
+                       if (base < BASE_FONT_SIZE || base > BASE_REAL_PIXEL)
+                               throw new IllegalArgumentException("Unrecognized base: " + base);
+
+                       LP_BASE = base;
+                       MOD_COUNT++;
+               }
+       }
+
+       /** Sets gap value for components that are "related".
+        * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        */
+       public static void setRelatedGap(UnitValue x, UnitValue y)
+       {
+               setUnitValue(new String[] {"r", "rel", "related"}, x, y);
+
+               RELATED_X = new BoundSize(x, x, null, "rel:rel");
+               RELATED_Y = new BoundSize(y, y, null, "rel:rel");
+       }
+
+       /** Sets gap value for components that are "unrelated".
+        * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        */
+       public static void setUnrelatedGap(UnitValue x, UnitValue y)
+       {
+               setUnitValue(new String[] {"u", "unrel", "unrelated"}, x, y);
+
+               UNRELATED_X = new BoundSize(x, x, null, "unrel:unrel");
+               UNRELATED_Y = new BoundSize(y, y, null, "unrel:unrel");
+       }
+
+       /** Sets paragraph gap value for components.
+        * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        */
+       public static void setParagraphGap(UnitValue x, UnitValue y)
+       {
+               setUnitValue(new String[] {"p", "para", "paragraph"}, x, y);
+       }
+
+       /** Sets gap value for components that are "intended".
+        * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        */
+       public static void setIndentGap(UnitValue x, UnitValue y)
+       {
+               setUnitValue(new String[] {"i", "ind", "indent"}, x, y);
+       }
+
+       /** Sets gap between two cells in the grid. Note that this is not a gap between component IN a cell, that has to be set
+        * on the component constraints. The value will be the min and preferred size of the gap.
+        * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+        */
+       public static void setGridCellGap(UnitValue x, UnitValue y)
+       {
+               if (x != null)
+                       DEF_HGAP = new BoundSize(x, x, null, null);
+
+               if (y != null)
+                       DEF_VGAP = new BoundSize(y, y, null, null);
+
+               MOD_COUNT++;
+       }
+
+       /** Sets the recommended minimum button width.
+        * @param width The recommended minimum button width.
+        */
+       public static void setMinimumButtonWidth(UnitValue width)
+       {
+               BUTT_WIDTH = width;
+               MOD_COUNT++;
+       }
+
+       /** Returns the recommended minimum button width depending on the current set platform.
+        * @return The recommended minimum button width depending on the current set platform.
+        */
+       public static UnitValue getMinimumButtonWidth()
+       {
+               return BUTT_WIDTH;
+       }
+
+       public static void setMinimumButtonPadding(UnitValue padding)
+       {
+               BUTT_PADDING = padding;
+               MOD_COUNT++;
+       }
+
+       public static UnitValue getMinimumButtonPadding()
+       {
+               return BUTT_PADDING;
+       }
+
+       public static float getMinimumButtonWidthIncludingPadding(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+       {
+               final int buttonMinWidth = getMinimumButtonWidth().getPixels(refValue, parent, comp);
+               if (comp != null && getMinimumButtonPadding() != null) {
+                       return Math.max(comp.getMinimumWidth(comp.getWidth()) + getMinimumButtonPadding().getPixels(refValue, parent, comp) * 2, buttonMinWidth);
+               } else {
+                       return buttonMinWidth;
+               }
+       }
+
+       /** Returns the unit value associated with the unit. (E.i. "related" or "indent"). Must be lower case.
+        * @param unit The unit string.
+        * @return The unit value associated with the unit. <code>null</code> for unrecognized units.
+        */
+       public static UnitValue getUnitValueX(String unit)
+       {
+               return HOR_DEFS.get(unit);
+       }
+
+       /** Returns the unit value associated with the unit. (E.i. "related" or "indent"). Must be lower case.
+        * @param unit The unit string.
+        * @return The unit value associated with the unit. <code>null</code> for unrecognized units.
+        */
+       public static UnitValue getUnitValueY(String unit)
+       {
+               return VER_DEFS.get(unit);
+       }
+
+       /** Sets the unit value associated with a unit string. This may be used to store values for new unit strings
+        * or modify old. Note that if a built in unit (such as "related") is modified all versions of it must be
+        * set (I.e. "r", "rel" and "related"). The build in values will be reset to the default ones if the platform
+        * is re-set.
+        * @param unitStrings The unit strings. E.g. "mu", "myunit". Will be converted to lower case and trimmed. Not <code>null</code>.
+        * @param x The value for the horizontal dimension. If <code>null</code> the value is not changed.
+        * @param y The value for the vertical dimension. Might be same object as for <code>x</code>. If <code>null</code> the value is not changed.
+        */
+       public static void setUnitValue(String[] unitStrings, UnitValue x, UnitValue y)
+       {
+               for (String unitString : unitStrings) {
+                       String s = unitString.toLowerCase().trim();
+                       if (x != null)
+                               HOR_DEFS.put(s, x);
+                       if (y != null)
+                               VER_DEFS.put(s, y);
+               }
+               MOD_COUNT++;
+       }
+
+       /** Understands ("r", "rel", "related") OR ("u", "unrel", "unrelated") OR ("i", "ind", "indent") OR ("p", "para", "paragraph").
+        */
+       static int convertToPixels(float value, String unit, boolean isHor, float ref, ContainerWrapper parent, ComponentWrapper comp)
+       {
+               UnitValue uv = (isHor ? HOR_DEFS : VER_DEFS).get(unit);
+               return uv != null ? Math.round(value * uv.getPixels(ref, parent, comp)) : UnitConverter.UNABLE;
+       }
+
+       /** Returns the order for the typical buttons in a standard button bar. It is one letter per button type.
+        * @return The button order.
+        * @see #setButtonOrder(String)
+        */
+       public static String getButtonOrder()
+       {
+               return BUTTON_FORMAT;
+       }
+
+       /** Sets the order for the typical buttons in a standard button bar. It is one letter per button type.
+        * <p>
+        * Letter in upper case will get the minimum button width that the {@link #getMinimumButtonWidth()} specifies
+        * and letters in lower case will get the width the current look&amp;feel specifies.
+        * <p>
+        * Gaps will never be added to before the first component or after the last component. However, '+' (push) will be
+        * applied before and after as well, but with a minimum size of 0 if first/last so there will not be a gap
+        * before or after.
+        * <p>
+        * If gaps are explicitly set on buttons they will never be reduced, but they may be increased.
+        * <p>
+        * These are the characters that can be used:
+        * <ul>
+        * <li><code>'L'</code> - Buttons with this style tag will statically end up on the left end of the bar.
+        * <li><code>'R'</code> - Buttons with this style tag will statically end up on the right end of the bar.
+        * <li><code>'H'</code> - A tag for the "help" button that normally is supposed to be on the right.
+        * <li><code>'E'</code> - A tag for the "help2" button that normally is supposed to be on the left.
+        * <li><code>'Y'</code> - A tag for the "yes" button.
+        * <li><code>'N'</code> - A tag for the "no" button.
+        * <li><code>'X'</code> - A tag for the "next &gt;" or "forward &gt;" button.
+        * <li><code>'B'</code> - A tag for the "&lt; back" or "&lt; previous" button.
+        * <li><code>'I'</code> - A tag for the "finish" button.
+        * <li><code>'A'</code> - A tag for the "apply" button.
+        * <li><code>'C'</code> - A tag for the "cancel" or "close" button.
+        * <li><code>'O'</code> - A tag for the "ok" or "done" button.
+        * <li><code>'U'</code> - All Uncategorized, Other, or "Unknown" buttons. Tag will be "other".
+        * <li><code>'+'</code> - A glue push gap that will take as much space as it can and at least an "unrelated" gap. (Platform dependent)
+        * <li><code>'_'</code> - (underscore) An "unrelated" gap. (Platform dependent)
+        * </ul>
+        * <p>
+        * Even though the style tags are normally applied to buttons this works with all components.
+        * <p>
+        * The normal style for MAC OS X is <code>"L_HE+U+NYBXCOA_I_R"</code>,
+        * for Windows is <code>"L_E+U+YNBXOCAH_I_R"</code>, and for GNOME is
+        * <code>"L_HE+UNYACBXO_I_R"</code>.
+        *
+        * @param order The new button order for the current platform.
+        */
+       public static void setButtonOrder(String order)
+       {
+               BUTTON_FORMAT = order;
+               MOD_COUNT++;
+       }
+
+       /** Returns the tag (used in the {@link CC}) for a char. The char is same as used in {@link #getButtonOrder()}.
+        * @param c The char. Must be lower case!
+        * @return The tag that corresponds to the char or <code>null</code> if the char is unrecognized.
+        */
+       static String getTagForChar(char c)
+       {
+               switch (c) {
+                       case 'o':
+                               return "ok";
+                       case 'c':
+                               return "cancel";
+                       case 'h':
+                               return "help";
+                       case 'e':
+                               return "help2";
+                       case 'y':
+                               return "yes";
+                       case 'n':
+                               return "no";
+                       case 'a':
+                               return "apply";
+                       case 'x':
+                               return "next";  // a.k.a forward
+                       case 'b':
+                               return "back";  // a.k.a. previous
+                       case 'i':
+                               return "finish";
+                       case 'l':
+                               return "left";
+                       case 'r':
+                               return "right";
+                       case 'u':
+                               return "other";
+                       default:
+                               return null;
+               }
+       }
+
+       /** Returns the platform recommended inter-cell gap in the horizontal (x) dimension..
+        * @return The platform recommended inter-cell gap in the horizontal (x) dimension..
+        */
+       public static BoundSize getGridGapX()
+       {
+               return DEF_HGAP;
+       }
+
+       /** Returns the platform recommended inter-cell gap in the vertical (x) dimension..
+        * @return The platform recommended inter-cell gap in the vertical (x) dimension..
+        */
+       public static BoundSize getGridGapY()
+       {
+               return DEF_VGAP;
+       }
+
+       /** Returns the default dialog insets depending of the current platform.
+        * @param side top == 0, left == 1, bottom = 2, right = 3.
+        * @return The insets. Never <code>null</code>.
+        */
+       public static UnitValue getDialogInsets(int side)
+       {
+               return DIALOG_INS[side];
+       }
+
+       /** Sets the default insets for a dialog. Values that are null will not be changed.
+        * @param top The top inset. May be <code>null</code>.
+        * @param left The left inset. May be <code>null</code>.
+        * @param bottom The bottom inset. May be <code>null</code>.
+        * @param right The right inset. May be <code>null</code>.
+        */
+       public static void setDialogInsets(UnitValue top, UnitValue left, UnitValue bottom, UnitValue right)
+       {
+               if (top != null)
+                       DIALOG_INS[0] = top;
+
+               if (left != null)
+                       DIALOG_INS[1] = left;
+
+               if (bottom != null)
+                       DIALOG_INS[2] = bottom;
+
+               if (right != null)
+                       DIALOG_INS[3] = right;
+
+               MOD_COUNT++;
+       }
+
+       /** Returns the default panel insets depending of the current platform.
+        * @param side top == 0, left == 1, bottom = 2, right = 3.
+        * @return The insets. Never <code>null</code>.
+        */
+       public static UnitValue getPanelInsets(int side)
+       {
+               return PANEL_INS[side];
+       }
+
+       /** Sets the default insets for a dialog. Values that are null will not be changed.
+        * @param top The top inset. May be <code>null</code>.
+        * @param left The left inset. May be <code>null</code>.
+        * @param bottom The bottom inset. May be <code>null</code>.
+        * @param right The right inset. May be <code>null</code>.
+        */
+       public static void setPanelInsets(UnitValue top, UnitValue left, UnitValue bottom, UnitValue right)
+       {
+               if (top != null)
+                       PANEL_INS[0] = top;
+
+               if (left != null)
+                       PANEL_INS[1] = left;
+
+               if (bottom != null)
+                       PANEL_INS[2] = bottom;
+
+               if (right != null)
+                       PANEL_INS[3] = right;
+
+               MOD_COUNT++;
+       }
+
+       /** Returns the percentage used for alignment for labels (0 is left, 50 is center and 100 is right).
+        * @return The percentage used for alignment for labels
+        */
+       public static float getLabelAlignPercentage()
+       {
+               return CUR_PLAF == MAC_OSX ? 1f : 0f;
+       }
+
+       /** Returns the default gap between two components that <b>are in the same cell</b>.
+        * @param comp The component that the gap is for. Never <code>null</code>.
+        * @param adjacentComp The adjacent component if any. May be <code>null</code>.
+        * @param adjacentSide What side the <code>adjacentComp</code> is on. {@link javax.swing.SwingUtilities#TOP} (1) or
+        * {@link javax.swing.SwingUtilities#LEFT} (2) or {@link javax.swing.SwingUtilities#BOTTOM} (3) or {@link javax.swing.SwingUtilities#RIGHT} (4).
+        * @param tag The tag string that the component might be tagged with in the component constraints. May be <code>null</code>.
+        * @param isLTR If it is left-to-right.
+        * @return The default gap between two components or <code>null</code> if there should be no gap.
+        */
+       static BoundSize getDefaultComponentGap(ComponentWrapper comp, ComponentWrapper adjacentComp, int adjacentSide, String tag, boolean isLTR)
+       {
+               if (GAP_PROVIDER != null)
+                       return GAP_PROVIDER.getDefaultGap(comp, adjacentComp, adjacentSide, tag, isLTR);
+
+               if (adjacentComp == null)
+                       return null;
+
+//             if (adjacentComp == null || adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.TOP)
+//                     return null;
+
+//             SwingConstants.RIGHT == 4, SwingConstants.LEFT == 2
+               return (adjacentSide == 2 || adjacentSide == 4) ? RELATED_X : RELATED_Y;
+       }
+
+       /** Returns the current gap provider or <code>null</code> if none is set and "related" should always be used.
+        * @return The current gap provider or <code>null</code> if none is set and "related" should always be used.
+        */
+       public static InCellGapProvider getGapProvider()
+       {
+               return GAP_PROVIDER;
+       }
+
+       /** Sets the current gap provider or <code>null</code> if none is set and "related" should always be used.
+        * @param provider The current gap provider or <code>null</code> if none is set and "related" should always be used.
+        */
+       public static void setGapProvider(InCellGapProvider provider)
+       {
+               GAP_PROVIDER = provider;
+       }
+
+       /** Returns how many times the defaults has been changed. This can be used as a light weight check to
+        * see if layout caches needs to be refreshed.
+        * @return How many times the defaults has been changed.
+        */
+       public static int getModCount()
+       {
+               return MOD_COUNT;
+       }
+
+       /** Tells all layout manager instances to revalidate and recalculated everything.
+        */
+       public void invalidate()
+       {
+               MOD_COUNT++;
+       }
+
+       /** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+        * @return The current default unit.
+        * @see UnitValue#PIXEL
+        * @see UnitValue#LPX
+        */
+       public static int getDefaultHorizontalUnit()
+       {
+               return DEF_H_UNIT;
+       }
+
+       /** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+        * @param unit The new default unit.
+        * @see UnitValue#PIXEL
+        * @see UnitValue#LPX
+        */
+       public static void setDefaultHorizontalUnit(int unit)
+       {
+               if (unit < UnitValue.PIXEL || unit > UnitValue.LABEL_ALIGN)
+                       throw new IllegalArgumentException("Illegal Unit: " + unit);
+
+               if (DEF_H_UNIT != unit) {
+                       DEF_H_UNIT = unit;
+                       MOD_COUNT++;
+               }
+       }
+
+       /** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+        * @return The current default unit.
+        * @see UnitValue#PIXEL
+        * @see UnitValue#LPY
+        */
+       public static int getDefaultVerticalUnit()
+       {
+               return DEF_V_UNIT;
+       }
+
+       /** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+        * @param unit The new default unit.
+        * @see UnitValue#PIXEL
+        * @see UnitValue#LPY
+        */
+       public static void setDefaultVerticalUnit(int unit)
+       {
+               if (unit < UnitValue.PIXEL || unit > UnitValue.LABEL_ALIGN)
+                       throw new IllegalArgumentException("Illegal Unit: " + unit);
+
+               if (DEF_V_UNIT != unit) {
+                       DEF_V_UNIT = unit;
+                       MOD_COUNT++;
+               }
+       }
+
+       /** The default alignment for rows. Pre v3.5 this was <code>false</code> but now it is
+        * <code>true</code>.
+        * @return The current value. Default is <code>true</code>.
+        * @since 3.5
+        */
+       public static boolean getDefaultRowAlignmentBaseline()
+       {
+               return dra;
+       }
+
+       /** The default alignment for rows. Pre v3.5 this was <code>false</code> but now it is
+        * <code>true</code>.
+        * @param b The new value. Default is <code>true</code> from v3.5.
+        * @since 3.5
+        */
+       public static void setDefaultRowAlignmentBaseline(boolean b)
+       {
+               dra = b;
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/ResizeConstraint.java b/srcjar/net/miginfocom/layout/ResizeConstraint.java
new file mode 100644 (file)
index 0000000..d4fff62
--- /dev/null
@@ -0,0 +1,94 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A parsed constraint that specifies how an entity (normally column/row or component) can shrink or
+ * grow compared to other entities.
+ */
+final class ResizeConstraint implements Externalizable
+{
+       static final Float WEIGHT_100 = 100f;
+
+       /** How flexible the entity should be, relative to other entities, when it comes to growing. <code>null</code> or
+        * zero mean it will never grow. An entity that has twice the growWeight compared to another entity will get twice
+        * as much of available space.
+        * <p>
+        * "grow" are only compared within the same "growPrio".
+        */
+       Float grow = null;
+
+       /** The relative priority used for determining which entities gets the extra space first.
+        */
+       int growPrio = 100;
+
+       Float shrink = WEIGHT_100;
+
+       int shrinkPrio = 100;
+
+       public ResizeConstraint()   // For Externalizable
+       {
+       }
+
+       ResizeConstraint(int shrinkPrio, Float shrinkWeight, int growPrio, Float growWeight)
+       {
+               this.shrinkPrio = shrinkPrio;
+               this.shrink = shrinkWeight;
+               this.growPrio = growPrio;
+               this.grow = growWeight;
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       @Override
+       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+
+       @Override
+       public void writeExternal(ObjectOutput out) throws IOException
+       {
+               if (getClass() == ResizeConstraint.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+}
diff --git a/srcjar/net/miginfocom/layout/UnitConverter.java b/srcjar/net/miginfocom/layout/UnitConverter.java
new file mode 100644 (file)
index 0000000..3a03ea2
--- /dev/null
@@ -0,0 +1,59 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/**
+ */
+public abstract class UnitConverter
+{
+       /** Value to return if this converter can not handle the <code>unit</code> sent in as an argument
+        * to the convert method.
+        */
+       public static final int UNABLE = -87654312;
+
+       /** Converts <code>value</code> to pixels.
+        * @param value The value to be converted.
+        * @param unit The unit of <code>value</code>. Never <code>null</code> and at least one character.
+        * @param refValue Some reference value that may of may not be used. If the unit is percent for instance this value
+        * is the value to take the percent from. Usually the size of the parent component in the appropriate dimension.
+        * @param isHor If the value is horizontal (<code>true</code>) or vertical (<code>false</code>).
+        * @param parent The parent of the target component that <code>value</code> is to be applied to.
+        * Might for instance be needed to get the screen that the component is on in a multi screen environment.
+        * <p>
+        * May be <code>null</code> in which case a "best guess" value should be returned.
+        * @param comp The component, if applicable, or <code>null</code> if none.
+        * @return The number of pixels if <code>unit</code> is handled by this converter, <code>UnitConverter.UNABLE</code> if not.
+        */
+       public abstract int convertToPixels(float value, String unit, boolean isHor, float refValue, ContainerWrapper parent, ComponentWrapper comp);
+}
diff --git a/srcjar/net/miginfocom/layout/UnitValue.java b/srcjar/net/miginfocom/layout/UnitValue.java
new file mode 100644 (file)
index 0000000..70d8f72
--- /dev/null
@@ -0,0 +1,691 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import java.beans.Encoder;
+import java.beans.Expression;
+import java.beans.PersistenceDelegate;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public final class UnitValue implements Serializable
+{
+       private static final HashMap<String, Integer> UNIT_MAP = new HashMap<String, Integer>(32);
+
+       private static final ArrayList<UnitConverter> CONVERTERS = new ArrayList<UnitConverter>();
+
+       /** An operation indicating a static value.
+        */
+       public static final int STATIC = 100;
+
+       /** An operation indicating a addition of two sub units.
+        */
+       public static final int ADD = 101; // Must have "sub-unit values"
+
+       /** An operation indicating a subtraction of two sub units
+        */
+       public static final int SUB = 102; // Must have "sub-unit values"
+
+       /** An operation indicating a multiplication of two sub units.
+        */
+       public static final int MUL = 103; // Must have "sub-unit values"
+
+       /** An operation indicating a division of two sub units.
+        */
+       public static final int DIV = 104; // Must have "sub-unit values"
+
+       /** An operation indicating the minimum of two sub units
+        */
+       public static final int MIN = 105; // Must have "sub-unit values"
+
+       /** An operation indicating the maximum of two sub units
+        */
+       public static final int MAX = 106; // Must have "sub-unit values"
+
+       /** An operation indicating the middle value of two sub units
+        */
+       public static final int MID = 107; // Must have "sub-unit values"
+
+
+
+
+       /** A unit indicating pixels.
+        */
+       public static final int PIXEL = 0;
+
+       /** A unit indicating logical horizontal pixels.
+        */
+       public static final int LPX = 1;
+
+       /** A unit indicating logical vertical pixels.
+        */
+       public static final int LPY = 2;
+
+       /** A unit indicating millimeters.
+        */
+       public static final int MM = 3;
+
+       /** A unit indicating centimeters.
+        */
+       public static final int CM = 4;
+
+       /** A unit indicating inches.
+        */
+       public static final int INCH = 5;
+
+       /** A unit indicating percent.
+        */
+       public static final int PERCENT = 6;
+
+       /** A unit indicating points.
+        */
+       public static final int PT = 7;
+
+       /** A unit indicating screen percentage width.
+        */
+       public static final int SPX = 8;
+
+       /** A unit indicating screen percentage height.
+        */
+       public static final int SPY = 9;
+
+       /** A unit indicating alignment.
+        */
+       public static final int ALIGN = 12;
+
+       /** A unit indicating minimum size.
+        */
+       public static final int MIN_SIZE = 13;
+
+       /** A unit indicating preferred size.
+        */
+       public static final int PREF_SIZE = 14;
+
+       /** A unit indicating maximum size.
+        */
+       public static final int MAX_SIZE = 15;
+
+       /** A unit indicating button size.
+        */
+       public static final int BUTTON = 16;
+
+       /** A unit indicating linking to x.
+        */
+       public static final int LINK_X = 18;   // First link
+
+       /** A unit indicating linking to y.
+        */
+       public static final int LINK_Y = 19;
+
+       /** A unit indicating linking to width.
+        */
+       public static final int LINK_W = 20;
+
+       /** A unit indicating linking to height.
+        */
+       public static final int LINK_H = 21;
+
+       /** A unit indicating linking to x2.
+        */
+       public static final int LINK_X2 = 22;
+
+       /** A unit indicating linking to y2.
+        */
+       public static final int LINK_Y2 = 23;
+
+       /** A unit indicating linking to x position on screen.
+        */
+       public static final int LINK_XPOS = 24;
+
+       /** A unit indicating linking to y position on screen.
+        */
+       public static final int LINK_YPOS = 25;    // Last link
+
+       /** A unit indicating a lookup.
+        */
+       public static final int LOOKUP = 26;
+
+       /** A unit indicating label alignment.
+        */
+       public static final int LABEL_ALIGN = 27;
+
+       private static final int IDENTITY = -1;
+
+       static {
+               UNIT_MAP.put("px", PIXEL);
+               UNIT_MAP.put("lpx", LPX);
+               UNIT_MAP.put("lpy", LPY);
+               UNIT_MAP.put("%", PERCENT);
+               UNIT_MAP.put("cm", CM);
+               UNIT_MAP.put("in", INCH);
+               UNIT_MAP.put("spx", SPX);
+               UNIT_MAP.put("spy", SPY);
+               UNIT_MAP.put("al", ALIGN);
+               UNIT_MAP.put("mm", MM);
+               UNIT_MAP.put("pt", PT);
+               UNIT_MAP.put("min", MIN_SIZE);
+               UNIT_MAP.put("minimum", MIN_SIZE);
+               UNIT_MAP.put("p", PREF_SIZE);
+               UNIT_MAP.put("pref", PREF_SIZE);
+               UNIT_MAP.put("max", MAX_SIZE);
+               UNIT_MAP.put("maximum", MAX_SIZE);
+               UNIT_MAP.put("button", BUTTON);
+               UNIT_MAP.put("label", LABEL_ALIGN);
+       }
+
+       static final UnitValue ZERO = new UnitValue(0, null, PIXEL, true, STATIC, null, null, "0px");
+       static final UnitValue TOP = new UnitValue(0, null, PERCENT, false, STATIC, null, null, "top");
+       static final UnitValue LEADING = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "leading");
+       static final UnitValue LEFT = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "left");
+       static final UnitValue CENTER = new UnitValue(50, null, PERCENT, true, STATIC, null, null, "center");
+       static final UnitValue TRAILING = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "trailing");
+       static final UnitValue RIGHT = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "right");
+       static final UnitValue BOTTOM = new UnitValue(100, null, PERCENT, false, STATIC, null, null, "bottom");
+       static final UnitValue LABEL = new UnitValue(0, null, LABEL_ALIGN, false, STATIC, null, null, "label");
+
+       static final UnitValue INF = new UnitValue(LayoutUtil.INF, null, PIXEL, true, STATIC, null, null, "inf");
+
+       static final UnitValue BASELINE_IDENTITY = new UnitValue(0, null, IDENTITY, false, STATIC, null, null, "baseline");
+
+       private final transient float value;
+       private final transient int unit;
+       private final transient int oper;
+       private final transient String unitStr;
+       private transient String linkId = null; // Should be final, but initializes in a sub method.
+       private final transient boolean isHor;
+       private final transient UnitValue[] subUnits;
+
+       // Pixel
+       public UnitValue(float value)  // If hor/ver does not matter.
+       {
+               this(value, null, PIXEL, true, STATIC, null, null, value + "px");
+       }
+
+       public UnitValue(float value, int unit, String createString)  // If hor/ver does not matter.
+       {
+               this(value, null, unit, true, STATIC, null, null, createString);
+       }
+
+        public UnitValue(float value, String unitStr, boolean isHor, int oper, String createString)
+       {
+               this(value, unitStr, -1, isHor, oper, null, null, createString);
+       }
+
+       UnitValue(boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
+       {
+               this(0, "", -1, isHor, oper, sub1, sub2, createString);
+               if (sub1 == null || sub2 == null)
+                       throw new IllegalArgumentException("Sub units is null!");
+       }
+
+       private UnitValue(float value, String unitStr, int unit, boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
+       {
+               if (oper < STATIC || oper > MID)
+                       throw new IllegalArgumentException("Unknown Operation: " + oper);
+
+               if (oper >= ADD && oper <= MID && (sub1 == null || sub2 == null))
+                       throw new IllegalArgumentException(oper + " Operation may not have null sub-UnitValues.");
+
+               this.value = value;
+               this.oper = oper;
+               this.isHor = isHor;
+               this.unitStr = unitStr;
+               this.unit = unitStr != null ? parseUnitString() : unit;
+               this.subUnits = sub1 != null && sub2 != null ? new UnitValue[] {sub1, sub2} : null;
+
+               LayoutUtil.putCCString(this, createString);    // "this" escapes!! Safe though.
+       }
+
+       /** Returns the size in pixels rounded.
+        * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
+        * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
+        * required to return any usable value if <code>null</code>.
+        * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
+        * connected to any component.
+        * @return The size in pixels.
+        */
+       public final int getPixels(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+       {
+               return Math.round(getPixelsExact(refValue, parent, comp));
+       }
+
+       private static final float[] SCALE = new float[] {25.4f, 2.54f, 1f, 0f, 72f};
+       /** Returns the size in pixels.
+        * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
+        * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
+        * required to return any usable value if <code>null</code>.
+        * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
+        * connected to any component.
+        * @return The size in pixels.
+        */
+       public final float getPixelsExact(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+       {
+               if (parent == null)
+                       return 1;
+
+               if (oper == STATIC) {
+                       switch (unit) {
+                               case PIXEL:
+                                       return value;
+
+                               case LPX:
+                               case LPY:
+                                       return parent.getPixelUnitFactor(unit == LPX) * value;
+
+                               case MM:
+                               case CM:
+                               case INCH:
+                               case PT:
+                                       float f = SCALE[unit - MM];
+                                       Float s = isHor ? PlatformDefaults.getHorizontalScaleFactor() : PlatformDefaults.getVerticalScaleFactor();
+                                       if (s != null)
+                                               f *= s;
+
+                                       return (isHor ? parent.getHorizontalScreenDPI() : parent.getVerticalScreenDPI()) * value / f;
+
+                               case PERCENT:
+                                       return value * refValue * 0.01f;
+
+                               case SPX:
+                               case SPY:
+                                       return (unit == SPX ? parent.getScreenWidth() : parent.getScreenHeight()) * value * 0.01f;
+
+                               case ALIGN:
+                                       Integer st = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.X : LinkHandler.Y);
+                                       Integer sz = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.WIDTH : LinkHandler.HEIGHT);
+                                       if (st == null || sz == null)
+                                               return 0;
+                                       return value * (Math.max(0, sz.intValue()) - refValue) + st;
+
+                               case MIN_SIZE:
+                                       if (comp == null)
+                                               return 0;
+                                       return isHor ? comp.getMinimumWidth(comp.getHeight()) : comp.getMinimumHeight(comp.getWidth());
+
+                               case PREF_SIZE:
+                                       if (comp == null)
+                                               return 0;
+                                       return isHor ? comp.getPreferredWidth(comp.getHeight()) : comp.getPreferredHeight(comp.getWidth());
+
+                               case MAX_SIZE:
+                                       if (comp == null)
+                                               return 0;
+                                       return isHor ? comp.getMaximumWidth(comp.getHeight()) : comp.getMaximumHeight(comp.getWidth());
+
+                               case BUTTON:
+                                       return PlatformDefaults.getMinimumButtonWidthIncludingPadding(refValue, parent, comp);
+
+                               case LINK_X:
+                               case LINK_Y:
+                               case LINK_W:
+                               case LINK_H:
+                               case LINK_X2:
+                               case LINK_Y2:
+                               case LINK_XPOS:
+                               case LINK_YPOS:
+                                       Integer v = LinkHandler.getValue(parent.getLayout(), getLinkTargetId(), unit - (unit >= LINK_XPOS ? LINK_XPOS : LINK_X));
+                                       if (v == null)
+                                               return 0;
+
+                                       if (unit == LINK_XPOS)
+                                               return parent.getScreenLocationX() + v;
+                                       if (unit == LINK_YPOS)
+                                               return parent.getScreenLocationY() + v;
+
+                                       return v;
+
+                               case LOOKUP:
+                                       float res = lookup(refValue, parent, comp);
+                                       if (res != UnitConverter.UNABLE)
+                                               return res;
+
+                               case LABEL_ALIGN:
+                                       return PlatformDefaults.getLabelAlignPercentage() * refValue;
+
+                               case IDENTITY:
+                       }
+                       throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
+               }
+
+               if (subUnits != null && subUnits.length == 2) {
+                       float r1 = subUnits[0].getPixelsExact(refValue, parent, comp);
+                       float r2 = subUnits[1].getPixelsExact(refValue, parent, comp);
+                       switch (oper) {
+                               case ADD:
+                                       return r1 + r2;
+                               case SUB:
+                                       return r1 - r2;
+                               case MUL:
+                                       return r1 * r2;
+                               case DIV:
+                                       return r1 / r2;
+                               case MIN:
+                                       return r1 < r2 ? r1 : r2;
+                               case MAX:
+                                       return r1 > r2 ? r1 : r2;
+                               case MID:
+                                       return (r1 + r2) * 0.5f;
+                       }
+               }
+
+               throw new IllegalArgumentException("Internal: Unknown Oper: " + oper);
+       }
+
+       private float lookup(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+       {
+               float res = UnitConverter.UNABLE;
+               for (int i = CONVERTERS.size() - 1; i >= 0; i--) {
+                       res = CONVERTERS.get(i).convertToPixels(value, unitStr, isHor, refValue, parent, comp);
+                       if (res != UnitConverter.UNABLE)
+                               return res;
+               }
+               return PlatformDefaults.convertToPixels(value, unitStr, isHor, refValue, parent, comp);
+       }
+
+       private int parseUnitString()
+       {
+               int len = unitStr.length();
+               if (len == 0)
+                       return isHor ? PlatformDefaults.getDefaultHorizontalUnit() : PlatformDefaults.getDefaultVerticalUnit();
+
+               Integer u = UNIT_MAP.get(unitStr);
+               if (u != null) {
+                       if (!isHor && (u == BUTTON || u == LABEL_ALIGN))
+                               throw new IllegalArgumentException("Not valid in vertical contexts: '" + unitStr + "'");
+
+                       return u;
+               }
+
+               if (unitStr.equals("lp"))
+                       return isHor ? LPX : LPY;
+
+               if (unitStr.equals("sp"))
+                       return isHor ? SPX : SPY;
+
+               if (lookup(0, null, null) != UnitConverter.UNABLE)    // To test so we can fail fast
+                       return LOOKUP;
+
+               // Only link left. E.g. "otherID.width"
+
+               int pIx = unitStr.indexOf('.');
+               if (pIx != -1) {
+                       linkId = unitStr.substring(0, pIx);
+                       String e = unitStr.substring(pIx + 1);
+
+                       if (e.equals("x"))
+                               return LINK_X;
+                       if (e.equals("y"))
+                               return LINK_Y;
+                       if (e.equals("w") || e.equals("width"))
+                               return LINK_W;
+                       if (e.equals("h") || e.equals("height"))
+                               return LINK_H;
+                       if (e.equals("x2"))
+                               return LINK_X2;
+                       if (e.equals("y2"))
+                               return LINK_Y2;
+                       if (e.equals("xpos"))
+                               return LINK_XPOS;
+                       if (e.equals("ypos"))
+                               return LINK_YPOS;
+               }
+
+               throw new IllegalArgumentException("Unknown keyword: " + unitStr);
+       }
+
+       final boolean isAbsolute()
+       {
+               switch (unit) {
+                       case PIXEL:
+                       case LPX:
+                       case LPY:
+                       case MM:
+                       case CM:
+                       case INCH:
+                       case PT:
+                               return true;
+
+                       case SPX:
+                       case SPY:
+                       case PERCENT:
+                       case ALIGN:
+                       case MIN_SIZE:
+                       case PREF_SIZE:
+                       case MAX_SIZE:
+                       case BUTTON:
+                       case LINK_X:
+                       case LINK_Y:
+                       case LINK_W:
+                       case LINK_H:
+                       case LINK_X2:
+                       case LINK_Y2:
+                       case LINK_XPOS:
+                       case LINK_YPOS:
+                       case LOOKUP:
+                       case LABEL_ALIGN:
+                               return false;
+
+                       case IDENTITY:
+               }
+               throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
+       }
+
+       final boolean isAbsoluteDeep()
+       {
+               if (subUnits != null) {
+                       for (UnitValue subUnit : subUnits) {
+                               if (subUnit.isAbsoluteDeep())
+                                       return true;
+                       }
+               }
+               return isAbsolute();
+       }
+
+       final boolean isLinked()
+       {
+               return linkId != null;
+       }
+
+       final boolean isLinkedDeep()
+       {
+               if (subUnits != null) {
+                       for (UnitValue subUnit : subUnits) {
+                               if (subUnit.isLinkedDeep())
+                                       return true;
+                       }
+               }
+               return isLinked();
+       }
+
+       final String getLinkTargetId()
+       {
+               return linkId;
+       }
+
+       final UnitValue getSubUnitValue(int i)
+       {
+               return subUnits[i];
+       }
+
+       final int getSubUnitCount()
+       {
+               return subUnits != null ? subUnits.length : 0;
+       }
+
+       public final UnitValue[] getSubUnits()
+       {
+               return subUnits != null ? subUnits.clone() : null;
+       }
+
+       public final int getUnit()
+       {
+               return unit;
+       }
+
+       public final String getUnitString()
+       {
+               return unitStr;
+       }
+
+       public final int getOperation()
+       {
+               return oper;
+       }
+
+       public final float getValue()
+       {
+               return value;
+       }
+
+       public final boolean isHorizontal()
+       {
+               return isHor;
+       }
+
+       @Override
+       final public String toString()
+       {
+               return getClass().getName() + ". Value=" + value + ", unit=" + unit + ", unitString: " + unitStr + ", oper=" + oper + ", isHor: " + isHor;
+       }
+
+       /** Returns the creation string for this object. Note that {@link LayoutUtil#setDesignTime(ContainerWrapper, boolean)} must be
+        * set to <code>true</code> for the creation strings to be stored.
+        * @return The constraint string or <code>null</code> if none is registered.
+        */
+       public final String getConstraintString()
+       {
+               return LayoutUtil.getCCString(this);
+       }
+
+       @Override
+       public final int hashCode()
+       {
+               return (int) (value * 12345) + (oper >>> 5) + unit >>> 17;
+       }
+
+       /** Adds a global unit converter that can convert from some <code>unit</code> to pixels.
+        * <p>
+        * This converter will be asked before the platform converter so the values for it (e.g. "related" and "unrelated")
+        * can be overridden. It is however not possible to override the built in ones (e.g. "mm", "pixel" or "lp").
+        * @param conv The converter. Not <code>null</code>.
+        */
+       public synchronized static void addGlobalUnitConverter(UnitConverter conv)
+       {
+               if (conv == null)
+                       throw new NullPointerException();
+               CONVERTERS.add(conv);
+       }
+
+       /** Removed the converter.
+        * @param unit The converter.
+        * @return If there was a converter found and thus removed.
+        */
+       public synchronized static boolean removeGlobalUnitConverter(UnitConverter unit)
+       {
+               return CONVERTERS.remove(unit);
+       }
+
+       /** Returns the global converters currently registered. The platform converter will not be in this list.
+        * @return The converters. Never <code>null</code>.
+        */
+       public synchronized static UnitConverter[] getGlobalUnitConverters()
+       {
+               return CONVERTERS.toArray(new UnitConverter[CONVERTERS.size()]);
+       }
+
+       /** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+        * @return The current default unit.
+        * @see #PIXEL
+        * @see #LPX
+        * @deprecated Use {@link PlatformDefaults#getDefaultHorizontalUnit()} and {@link PlatformDefaults#getDefaultVerticalUnit()} instead.
+        */
+       public static int getDefaultUnit()
+       {
+               return PlatformDefaults.getDefaultHorizontalUnit();
+       }
+
+       /** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+        * @param unit The new default unit.
+        * @see #PIXEL
+        * @see #LPX
+        * @deprecated Use {@link PlatformDefaults#setDefaultHorizontalUnit(int)} and {@link PlatformDefaults#setDefaultVerticalUnit(int)} instead.
+        */
+       public static void setDefaultUnit(int unit)
+       {
+               PlatformDefaults.setDefaultHorizontalUnit(unit);
+               PlatformDefaults.setDefaultVerticalUnit(unit);
+       }
+
+       static {
+        if(LayoutUtil.HAS_BEANS){
+            LayoutUtil.setDelegate(UnitValue.class, new PersistenceDelegate() {
+                @Override
+                protected Expression instantiate(Object oldInstance, Encoder out)
+                {
+                    UnitValue uv = (UnitValue) oldInstance;
+                    String cs = uv.getConstraintString();
+                    if (cs == null)
+                        throw new IllegalStateException("Design time must be on to use XML persistence. See LayoutUtil.");
+
+                    return new Expression(oldInstance, ConstraintParser.class, "parseUnitValueOrAlign", new Object[] {
+                            uv.getConstraintString(), (uv.isHorizontal() ? Boolean.TRUE : Boolean.FALSE), null
+                    });
+                }
+            });
+        }
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private static final long serialVersionUID = 1L;
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       private void writeObject(ObjectOutputStream out) throws IOException
+       {
+               if (getClass() == UnitValue.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+
+       private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+}
diff --git a/srcjar/net/miginfocom/swing/MigLayout.java b/srcjar/net/miginfocom/swing/MigLayout.java
new file mode 100644 (file)
index 0000000..fdd7718
--- /dev/null
@@ -0,0 +1,786 @@
+package net.miginfocom.swing;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import net.miginfocom.layout.*;
+
+import javax.swing.*;
+import javax.swing.Timer;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.util.*;
+
+/** A very flexible layout manager.
+ * <p>
+ * Read the documentation that came with this layout manager for information on usage.
+ */
+public class MigLayout implements LayoutManager2, Externalizable
+{
+       // ******** Instance part ********
+
+       /** The component to string constraints mappings.
+        */
+       private final Map<Component, Object> scrConstrMap = new IdentityHashMap<Component, Object>(8);
+
+       /** Hold the serializable text representation of the constraints.
+        */
+       private Object layoutConstraints = "", colConstraints = "", rowConstraints = "";    // Should never be null!
+
+       // ******** Transient part ********
+
+       private transient ContainerWrapper cacheParentW = null;
+
+       private transient final Map<ComponentWrapper, CC> ccMap = new HashMap<ComponentWrapper, CC>(8);
+       private transient javax.swing.Timer debugTimer = null;
+
+       private transient LC lc = null;
+       private transient AC colSpecs = null, rowSpecs = null;
+       private transient Grid grid = null;
+       private transient int lastModCount = PlatformDefaults.getModCount();
+       private transient int lastHash = -1;
+       private transient Dimension lastInvalidSize = null;
+       private transient boolean lastWasInvalid = false;  // Added in 3.7.1. May have regressions
+       private transient Dimension lastParentSize = null;
+
+       private transient ArrayList<LayoutCallback> callbackList = null;
+
+       private transient boolean dirty = true;
+
+       /** Constructor with no constraints.
+        */
+       public MigLayout()
+       {
+               this("", "", "");
+       }
+
+       /** Constructor.
+        * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
+        */
+       public MigLayout(String layoutConstraints)
+       {
+               this(layoutConstraints, "", "");
+       }
+
+       /** Constructor.
+        * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
+        * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
+        */
+       public MigLayout(String layoutConstraints, String colConstraints)
+       {
+               this(layoutConstraints, colConstraints, "");
+       }
+
+       /** Constructor.
+        * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
+        * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
+        * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as "".
+        */
+       public MigLayout(String layoutConstraints, String colConstraints, String rowConstraints)
+       {
+               setLayoutConstraints(layoutConstraints);
+               setColumnConstraints(colConstraints);
+               setRowConstraints(rowConstraints);
+       }
+
+       /** Constructor.
+        * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
+        */
+       public MigLayout(LC layoutConstraints)
+       {
+               this(layoutConstraints, null, null);
+       }
+
+       /** Constructor.
+        * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
+        * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
+        */
+       public MigLayout(LC layoutConstraints, AC colConstraints)
+       {
+               this(layoutConstraints, colConstraints, null);
+       }
+
+       /** Constructor.
+        * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
+        * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
+        * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as an empty constraint.
+        */
+       public MigLayout(LC layoutConstraints, AC colConstraints, AC rowConstraints)
+       {
+               setLayoutConstraints(layoutConstraints);
+               setColumnConstraints(colConstraints);
+               setRowConstraints(rowConstraints);
+       }
+
+       /** Returns layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
+        * to the constructor or set with {@link #setLayoutConstraints(Object)}.
+        * @return The layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
+        * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
+        */
+       public Object getLayoutConstraints()
+       {
+               return layoutConstraints;
+       }
+
+       /** Sets the layout constraints for the layout manager instance as a String.
+        * <p>
+        * See the class JavaDocs for information on how this string is formatted.
+        * @param constr The layout constraints as a String or {@link net.miginfocom.layout.LC} representation. <code>null</code> is converted to <code>""</code> for storage.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       public void setLayoutConstraints(Object constr)
+       {
+               if (constr == null || constr instanceof String) {
+                       constr = ConstraintParser.prepare((String) constr);
+                       lc = ConstraintParser.parseLayoutConstraint((String) constr);
+               } else if (constr instanceof LC) {
+                       lc = (LC) constr;
+               } else {
+                       throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
+               }
+               layoutConstraints = constr;
+               dirty = true;
+       }
+
+       /** Returns the column layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC}.
+        * @return The column constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC} depending what was sent in
+        * to the constructor or set with {@link #setColumnConstraints(Object)}. Never <code>null</code>.
+        */
+       public Object getColumnConstraints()
+       {
+               return colConstraints;
+       }
+
+       /** Sets the column layout constraints for the layout manager instance as a String.
+        * <p>
+        * See the class JavaDocs for information on how this string is formatted.
+        * @param constr The column layout constraints as a String or {@link net.miginfocom.layout.AC} representation. <code>null</code> is converted to <code>""</code> for storage.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       public void setColumnConstraints(Object constr)
+       {
+               if (constr == null || constr instanceof String) {
+                       constr = ConstraintParser.prepare((String) constr);
+                       colSpecs = ConstraintParser.parseColumnConstraints((String) constr);
+               } else if (constr instanceof AC) {
+                       colSpecs = (AC) constr;
+               } else {
+                       throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
+               }
+               colConstraints = constr;
+               dirty = true;
+       }
+
+       /** Returns the row layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC}.
+        * @return The row constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC} depending what was sent in
+        * to the constructor or set with {@link #setRowConstraints(Object)}. Never <code>null</code>.
+        */
+       public Object getRowConstraints()
+       {
+               return rowConstraints;
+       }
+
+       /** Sets the row layout constraints for the layout manager instance as a String.
+        * <p>
+        * See the class JavaDocs for information on how this string is formatted.
+        * @param constr The row layout constraints as a String or {@link net.miginfocom.layout.AC} representation. <code>null</code> is converted to <code>""</code> for storage.
+        * @throws RuntimeException if the constraint was not valid.
+        */
+       public void setRowConstraints(Object constr)
+       {
+               if (constr == null || constr instanceof String) {
+                       constr = ConstraintParser.prepare((String) constr);
+                       rowSpecs = ConstraintParser.parseRowConstraints((String) constr);
+               } else if (constr instanceof AC) {
+                       rowSpecs = (AC) constr;
+               } else {
+                       throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
+               }
+               rowConstraints = constr;
+               dirty = true;
+       }
+
+       /** Returns a shallow copy of the constraints map.
+        * @return A  shallow copy of the constraints map. Never <code>null</code>.
+        */
+       public Map<Component, Object> getConstraintMap()
+       {
+               return new IdentityHashMap<Component, Object>(scrConstrMap);
+       }
+
+       /** Sets the constraints map.
+        * @param map The map. Will be copied.
+        */
+       public void setConstraintMap(Map<Component, Object> map)
+       {
+               scrConstrMap.clear();
+               ccMap.clear();
+               for (Map.Entry<Component, Object> e : map.entrySet())
+                       setComponentConstraintsImpl(e.getKey(), e.getValue(), true);
+       }
+
+       /** Returns the component constraints as a String representation. This string is the exact string as set with {@link #setComponentConstraints(java.awt.Component, Object)}
+        * or set when adding the component to the parent component.
+        * <p>
+        * See the class JavaDocs for information on how this string is formatted.
+        * @param comp The component to return the constraints for.
+        * @return The component constraints as a String representation or <code>null</code> if the component is not registered
+        * with this layout manager. The returned values is either a String or a {@link net.miginfocom.layout.CC}
+        * depending on what constraint was sent in when the component was added. May be <code>null</code>.
+        */
+       public Object getComponentConstraints(Component comp)
+       {
+               synchronized(comp.getParent().getTreeLock()) {
+                       return scrConstrMap.get(comp);
+               }
+       }
+
+       /** Sets the component constraint for the component that already must be handled by this layout manager.
+        * <p>
+        * See the class JavaDocs for information on how this string is formatted.
+        * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
+        * @param comp The component to set the constraints for.
+        * @throws RuntimeException if the constraint was not valid.
+        * @throws IllegalArgumentException If the component is not handling the component.
+        */
+       public void setComponentConstraints(Component comp, Object constr)
+       {
+               setComponentConstraintsImpl(comp, constr, false);
+       }
+
+       /** Sets the component constraint for the component that already must be handled by this layout manager.
+        * <p>
+        * See the class JavaDocs for information on how this string is formatted.
+        * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
+        * @param comp The component to set the constraints for.
+        * @param noCheck Does not check if the component is handled if true
+        * @throws RuntimeException if the constraint was not valid.
+        * @throws IllegalArgumentException If the component is not handling the component.
+        */
+       private void setComponentConstraintsImpl(Component comp, Object constr, boolean noCheck)
+       {
+               Container parent = comp.getParent();
+               synchronized(parent != null ? parent.getTreeLock() : new Object()) { // 3.7.2. No sync if not added to a hierarchy. Defeats a NPE.
+                       if (noCheck == false && scrConstrMap.containsKey(comp) == false)
+                               throw new IllegalArgumentException("Component must already be added to parent!");
+
+                       ComponentWrapper cw = new SwingComponentWrapper(comp);
+
+                       if (constr == null || constr instanceof String) {
+                               String cStr = ConstraintParser.prepare((String) constr);
+
+                               scrConstrMap.put(comp, constr);
+                               ccMap.put(cw, ConstraintParser.parseComponentConstraint(cStr));
+
+                       } else if (constr instanceof CC) {
+
+                               scrConstrMap.put(comp, constr);
+                               ccMap.put(cw, (CC) constr);
+
+                       } else {
+                               throw new IllegalArgumentException("Constraint must be String or ComponentConstraint: " + constr.getClass().toString());
+                       }
+
+                       dirty = true;
+               }
+       }
+
+       /** Returns if this layout manager is currently managing this component.
+        * @param c The component to check. If <code>null</code> then <code>false</code> will be returned.
+        * @return If this layout manager is currently managing this component.
+        */
+       public boolean isManagingComponent(Component c)
+       {
+               return scrConstrMap.containsKey(c);
+       }
+
+       /** Adds the callback function that will be called at different stages of the layout cycle.
+        * @param callback The callback. Not <code>null</code>.
+        */
+       public void addLayoutCallback(LayoutCallback callback)
+       {
+               if (callback == null)
+                       throw new NullPointerException();
+
+               if (callbackList == null)
+                       callbackList = new ArrayList<LayoutCallback>(1);
+
+               callbackList.add(callback);
+
+               grid = null;
+       }
+
+       /** Removes the callback if it exists.
+        * @param callback The callback. May be <code>null</code>.
+        */
+       public void removeLayoutCallback(LayoutCallback callback)
+       {
+               if (callbackList != null)
+                       callbackList.remove(callback);
+       }
+
+       /** Sets the debugging state for this layout manager instance. If debug is turned on a timer will repaint the last laid out parent
+        * with debug information on top.
+        * <p>
+        * Red fill and dashed red outline is used to indicate occupied cells in the grid. Blue dashed outline indicate
+        * component bounds set.
+        * <p>
+        * Note that debug can also be set on the layout constraints. There it will be persisted. The value set here will not. See the class
+        * JavaDocs for information.
+        * @param parentW The parent to set debug for.
+        * @param b <code>true</code> means debug is turned on.
+        */
+       private void setDebug(final ComponentWrapper parentW, boolean b)
+       {
+               if (b && (debugTimer == null || debugTimer.getDelay() != getDebugMillis())) {
+                       if (debugTimer != null)
+                               debugTimer.stop();
+
+                       ContainerWrapper pCW = parentW.getParent();
+                       final Component parent = pCW != null ? (Component) pCW.getComponent() : null;
+
+                       debugTimer = new Timer(getDebugMillis(), new MyDebugRepaintListener());
+
+                       if (parent != null) {
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       @Override
+                                       public void run() {
+                                               Container p = parent.getParent();
+                                               if (p != null) {
+                                                       if (p instanceof JComponent) {
+                                                               ((JComponent) p).revalidate();
+                                                       } else {
+                                                               parent.invalidate();
+                                                               p.validate();
+                                                       }
+                                               }
+                                       }
+                               });
+                       }
+
+                       debugTimer.setInitialDelay(100);
+                       debugTimer.start();
+
+               } else if (!b && debugTimer != null) {
+                       debugTimer.stop();
+                       debugTimer = null;
+               }
+       }
+
+       /** Returns the current debugging state.
+        * @return The current debugging state.
+        */
+       private boolean getDebug()
+       {
+               return debugTimer != null;
+       }
+
+       /** Returns the debug millis. Combines the value from {@link net.miginfocom.layout.LC#getDebugMillis()} and {@link net.miginfocom.layout.LayoutUtil#getGlobalDebugMillis()}
+        * @return The combined value.
+        */
+       private int getDebugMillis()
+       {
+               int globalDebugMillis = LayoutUtil.getGlobalDebugMillis();
+               return globalDebugMillis > 0 ? globalDebugMillis : lc.getDebugMillis();
+       }
+
+       /** Check if something has changed and if so recreate it to the cached objects.
+        * @param parent The parent that is the target for this layout manager.
+        */
+       private void checkCache(Container parent)
+       {
+               if (parent == null)
+                       return;
+
+               if (dirty)
+                       grid = null;
+
+               cleanConstraintMaps(parent);
+
+               // Check if the grid is valid
+               int mc = PlatformDefaults.getModCount();
+               if (lastModCount != mc) {
+                       grid = null;
+                       lastModCount = mc;
+               }
+
+               if (!parent.isValid()) {
+                       if (!lastWasInvalid) {
+                               lastWasInvalid = true;
+
+                               int hash = 0;
+                               boolean resetLastInvalidOnParent = false; // Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
+                               for (ComponentWrapper wrapper : ccMap.keySet()) {
+                                       Object component = wrapper.getComponent();
+                                       if (component instanceof JTextArea || component instanceof JEditorPane)
+                                               resetLastInvalidOnParent = true;
+
+                                       hash ^= wrapper.getLayoutHashCode();
+                                       hash += 285134905;
+                               }
+                               if (resetLastInvalidOnParent)
+                                       resetLastInvalidOnParent(parent);
+
+                               if (hash != lastHash) {
+                                       grid = null;
+                                       lastHash = hash;
+                               }
+
+                               Dimension ps = parent.getSize();
+                               if (lastInvalidSize == null || !lastInvalidSize.equals(ps)) {
+                                       grid = null;
+                                       lastInvalidSize = ps;
+                               }
+                       }
+               } else {
+                       lastWasInvalid = false;
+               }
+
+               ContainerWrapper par = checkParent(parent);
+
+               setDebug(par, getDebugMillis() > 0);
+
+               if (grid == null)
+                       grid = new Grid(par, lc, rowSpecs, colSpecs, ccMap, callbackList);
+
+               dirty = false;
+       }
+
+       /** Checks so all components in ccMap actually exist in the parent's collection. Removes
+        * any references that don't.
+        * @param parent The parent to compare ccMap against. Never null.
+        */
+       private void cleanConstraintMaps(Container parent)
+       {
+               HashSet<Component> parentCompSet = new HashSet<Component>(Arrays.asList(parent.getComponents()));
+
+               Iterator<Map.Entry<ComponentWrapper, CC>> it = ccMap.entrySet().iterator();
+               while(it.hasNext()) {
+                       Component c = (Component) it.next().getKey().getComponent();
+                       if (parentCompSet.contains(c) == false) {
+                               it.remove();
+                               scrConstrMap.remove(c);
+                       }
+               }
+       }
+
+       /**
+        * @since 3.7.3
+        */
+       private void resetLastInvalidOnParent(Container parent)
+       {
+               while (parent != null) {
+                       LayoutManager layoutManager = parent.getLayout();
+                       if (layoutManager instanceof MigLayout) {
+                               ((MigLayout) layoutManager).lastWasInvalid = false;
+                       }
+                       parent = parent.getParent();
+               }
+       }
+
+       private ContainerWrapper checkParent(Container parent)
+       {
+               if (parent == null)
+                       return null;
+
+               if (cacheParentW == null || cacheParentW.getComponent() != parent)
+                       cacheParentW = new SwingContainerWrapper(parent);
+
+               return cacheParentW;
+       }
+
+       private long lastSize = 0;
+
+       @Override
+       public void layoutContainer(final Container parent)
+       {
+               synchronized(parent.getTreeLock()) {
+                       checkCache(parent);
+
+                       Insets i = parent.getInsets();
+                       int[] b = new int[] {
+                                       i.left,
+                                       i.top,
+                                       parent.getWidth() - i.left - i.right,
+                                       parent.getHeight() - i.top - i.bottom
+                       };
+
+                       if (grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug())) {
+                               grid = null;
+                               checkCache(parent);
+                               grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug());
+                       }
+
+                       long newSize = grid.getHeight()[1] + (((long) grid.getWidth()[1]) << 32);
+                       if (lastSize != newSize) {
+                               lastSize = newSize;
+                               final ContainerWrapper containerWrapper = checkParent(parent);
+                               Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class, (Component)containerWrapper.getComponent()));
+                               if (win != null) {
+                                  if (win.isVisible()) {
+                                          SwingUtilities.invokeLater(new Runnable() {
+                                                  @Override
+                                                  public void run() {
+                                                          adjustWindowSize(containerWrapper);
+                                                  }
+                                          });
+                                  } else {
+                                          adjustWindowSize(containerWrapper);
+                                  }
+                               }
+                       }
+                       lastInvalidSize = null;
+               }
+       }
+
+       /** Checks the parent window/popup if its size is within parameters as set by the LC.
+        * @param parent The parent who's window to possibly adjust the size for.
+        */
+       private void adjustWindowSize(ContainerWrapper parent)
+       {
+               BoundSize wBounds = lc.getPackWidth();
+               BoundSize hBounds = lc.getPackHeight();
+
+               if (wBounds == BoundSize.NULL_SIZE && hBounds == BoundSize.NULL_SIZE)
+                       return;
+
+               Container packable = getPackable((Component) parent.getComponent());
+
+               if (packable != null) {
+
+                       Component pc = (Component) parent.getComponent();
+
+                       Container c = pc instanceof Container ? (Container) pc : pc.getParent();
+                       for (; c != null; c = c.getParent()) {
+                               LayoutManager layout = c.getLayout();
+                               if (layout instanceof BoxLayout || layout instanceof OverlayLayout)
+                                       ((LayoutManager2) layout).invalidateLayout(c);
+                       }
+
+                       Dimension prefSize = packable.getPreferredSize();
+                       int targW = constrain(checkParent(packable), packable.getWidth(), prefSize.width, wBounds);
+                       int targH = constrain(checkParent(packable), packable.getHeight(), prefSize.height, hBounds);
+
+                       Point p = packable.isShowing() ? packable.getLocationOnScreen() : packable.getLocation();
+
+                       int x = Math.round(p.x - ((targW - packable.getWidth()) * (1 - lc.getPackWidthAlign())));
+                       int y = Math.round(p.y - ((targH - packable.getHeight()) * (1 - lc.getPackHeightAlign())));
+
+                       if (packable instanceof JPopupMenu) {
+                               JPopupMenu popupMenu = (JPopupMenu) packable;
+                               popupMenu.setVisible(false);
+                               popupMenu.setPopupSize(targW, targH);
+                               Component invoker = popupMenu.getInvoker();
+                               Point popPoint = new Point(x, y);
+                               SwingUtilities.convertPointFromScreen(popPoint, invoker);
+                               ((JPopupMenu) packable).show(invoker, popPoint.x, popPoint.y);
+
+                               packable.setPreferredSize(null); // Reset preferred size so we don't read it again.
+
+                       } else {
+                               packable.setBounds(x, y, targW, targH);
+                       }
+               }
+       }
+
+       /** Returns a high level window or popup to pack, if any.
+        * @return May be null.
+        */
+       private Container getPackable(Component comp)
+       {
+               JPopupMenu popup = findType(JPopupMenu.class, comp);
+               if (popup != null) { // Lightweight/HeavyWeight popup must be handled separately
+                       Container popupComp = popup;
+                       while (popupComp != null) {
+                               if (popupComp.getClass().getName().contains("HeavyWeightWindow"))
+                                       return popupComp; // Return the heavy weight window for normal processing
+                               popupComp = popupComp.getParent();
+                       }
+                       return popup; // Return the JPopup.
+               }
+
+               return findType(Window.class, comp);
+       }
+
+       public static <E> E findType(Class<E> clazz, Component comp)
+       {
+               while (comp != null && !clazz.isInstance(comp))
+                       comp = comp.getParent();
+
+               return (E) comp;
+       }
+
+
+       private int constrain(ContainerWrapper parent, int winSize, int prefSize, BoundSize constrain)
+       {
+               if (constrain == null)
+                       return winSize;
+
+               int retSize = winSize;
+               UnitValue wUV = constrain.getPreferred();
+               if (wUV != null)
+                       retSize = wUV.getPixels(prefSize, parent, parent);
+
+               retSize = constrain.constrain(retSize, prefSize, parent);
+
+               return constrain.getGapPush() ? Math.max(winSize, retSize) : retSize;
+       }
+
+       @Override
+       public Dimension minimumLayoutSize(Container parent)
+       {
+               synchronized(parent.getTreeLock()) {
+                       return getSizeImpl(parent, LayoutUtil.MIN);
+               }
+       }
+
+       @Override
+       public Dimension preferredLayoutSize(Container parent)
+       {
+               synchronized(parent.getTreeLock()) {
+           if (lastParentSize == null || !parent.getSize().equals(lastParentSize)) {
+               for (ComponentWrapper wrapper : ccMap.keySet()) {
+                      if (wrapper.getContentBias() != -1) {
+                       layoutContainer(parent);
+                       break;
+                   }
+               }
+           }
+
+           lastParentSize = parent.getSize();
+           return getSizeImpl(parent, LayoutUtil.PREF);
+               }
+       }
+
+       @Override
+       public Dimension maximumLayoutSize(Container parent)
+       {
+               return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+       }
+
+       // Implementation method that does the job.
+       private Dimension getSizeImpl(Container parent, int sizeType)
+       {
+               checkCache(parent);
+
+               Insets i = parent.getInsets();
+
+               int w = LayoutUtil.getSizeSafe(grid != null ? grid.getWidth() : null, sizeType) + i.left + i.right;
+               int h = LayoutUtil.getSizeSafe(grid != null ? grid.getHeight() : null, sizeType) + i.top + i.bottom;
+
+               return new Dimension(w, h);
+       }
+
+       @Override
+       public float getLayoutAlignmentX(Container parent)
+       {
+               return lc != null && lc.getAlignX() != null ? lc.getAlignX().getPixels(1, checkParent(parent), null) : 0;
+       }
+
+       @Override
+       public float getLayoutAlignmentY(Container parent)
+       {
+               return lc != null && lc.getAlignY() != null ? lc.getAlignY().getPixels(1, checkParent(parent), null) : 0;
+       }
+
+       @Override
+       public void addLayoutComponent(String s, Component comp)
+       {
+               addLayoutComponent(comp, s);
+       }
+
+       @Override
+       public void addLayoutComponent(Component comp, Object constraints)
+       {
+               synchronized(comp.getParent().getTreeLock()) {
+                       setComponentConstraintsImpl(comp, constraints, true);
+               }
+       }
+
+       @Override
+       public void removeLayoutComponent(Component comp)
+       {
+               synchronized(comp.getParent().getTreeLock()) {
+                       scrConstrMap.remove(comp);
+                       ccMap.remove(new SwingComponentWrapper(comp));
+                       grid = null; // To clear references
+               }
+       }
+
+       @Override
+       public void invalidateLayout(Container target)
+       {
+               dirty = true;
+       }
+
+       // ************************************************
+       // Persistence Delegate and Serializable combined.
+       // ************************************************
+
+       private Object readResolve() throws ObjectStreamException
+       {
+               return LayoutUtil.getSerializedObject(this);
+       }
+
+       @Override
+       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+       {
+               LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+       }
+
+       @Override
+       public void writeExternal(ObjectOutput out) throws IOException
+       {
+               if (getClass() == MigLayout.class)
+                       LayoutUtil.writeAsXML(out, this);
+       }
+
+       private class MyDebugRepaintListener implements ActionListener
+       {
+               @Override
+               public void actionPerformed(ActionEvent e)
+               {
+                       if (grid != null) {
+                               Component comp = (Component) grid.getContainer().getComponent();
+                               if (comp.isShowing()) {
+                                       grid.paintDebug();
+                                       return;
+                               }
+                       }
+                       debugTimer.stop();
+                       debugTimer = null;
+               }
+       }
+}
\ No newline at end of file
diff --git a/srcjar/net/miginfocom/swing/SwingComponentWrapper.java b/srcjar/net/miginfocom/swing/SwingComponentWrapper.java
new file mode 100644 (file)
index 0000000..46a78fd
--- /dev/null
@@ -0,0 +1,679 @@
+package net.miginfocom.swing;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import net.miginfocom.layout.ComponentWrapper;
+import net.miginfocom.layout.ContainerWrapper;
+import net.miginfocom.layout.LayoutUtil;
+import net.miginfocom.layout.PlatformDefaults;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.text.JTextComponent;
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.util.IdentityHashMap;
+import java.util.StringTokenizer;
+
+/**
+ */
+public class SwingComponentWrapper implements ComponentWrapper
+{
+       private static boolean maxSet = false;
+
+       private static boolean vp = true;
+
+       /** Debug color for component bounds outline.
+        */
+       private static final Color DB_COMP_OUTLINE = new Color(0, 0, 200);
+
+       /** Property to use in LAF settings and as JComponent client property
+        * to specify the visual padding.
+        * <p>
+        */
+       private static final String VISUAL_PADDING_PROPERTY = net.miginfocom.layout.PlatformDefaults.VISUAL_PADDING_PROPERTY;
+
+       private final Component c;
+       private int compType = TYPE_UNSET;
+       private Boolean bl = null;
+       private boolean prefCalled = false;
+
+       public SwingComponentWrapper(Component c)
+       {
+               this.c = c;
+       }
+
+       @Override
+       public final int getBaseline(int width, int height)
+       {
+               int h = height;
+               int[] visPad = getVisualPadding();
+               if (h < 0) {
+                       h = c.getHeight();
+               } else if (visPad != null) {
+                       h = height + visPad[0] + visPad[2];
+               }
+               int baseLine = c.getBaseline(width < 0 ? c.getWidth() : width, h);
+               if (baseLine != -1 && visPad != null)
+                       baseLine -= visPad[0];
+
+               return baseLine;
+       }
+
+       @Override
+       public final Object getComponent()
+       {
+               return c;
+       }
+
+       /** Cache.
+        */
+       private final static IdentityHashMap<FontMetrics, Point.Float> FM_MAP = new IdentityHashMap<FontMetrics, Point.Float>(4);
+       private final static Font SUBST_FONT = new Font("sansserif", Font.PLAIN, 11);
+
+       @Override
+       public final float getPixelUnitFactor(boolean isHor)
+       {
+               switch (PlatformDefaults.getLogicalPixelBase()) {
+                       case PlatformDefaults.BASE_FONT_SIZE:
+                               Font font = c.getFont();
+                               FontMetrics fm = c.getFontMetrics(font != null ? font : SUBST_FONT);
+                               Point.Float p = FM_MAP.get(fm);
+                               if (p == null) {
+                                       Rectangle2D r = fm.getStringBounds("X", c.getGraphics());
+                                       p = new Point.Float(((float) r.getWidth()) / 6f, ((float) r.getHeight()) / 13.27734375f);
+                                       FM_MAP.put(fm, p);
+                               }
+                               return isHor ? p.x : p.y;
+
+                       case PlatformDefaults.BASE_SCALE_FACTOR:
+
+                               Float s = isHor ? PlatformDefaults.getHorizontalScaleFactor() : PlatformDefaults.getVerticalScaleFactor();
+                               float scaleFactor = (s != null) ? s : 1f;
+
+                               // Swing in Java 9 scales automatically using the system scale factor(s) that the
+                               // user can change in the system settings (Windows: Control Panel; Mac: System Preferences).
+                               // Each connected screen may use its own scale factor
+                               // (e.g. 1.5 for primary 4K 40inch screen and 1.0 for secondary HD screen).
+                               float screenScale = isJava9orLater
+                                       ? 1f // use system scale factor(s)
+                                       : (float) (isHor ? getHorizontalScreenDPI() : getVerticalScreenDPI()) / (float) PlatformDefaults.getDefaultDPI();
+                               return scaleFactor * screenScale;
+
+                       default:
+                               return 1f;
+               }
+       }
+
+       private static boolean isJava9orLater;
+       static {
+               try {
+                       // Java 9 version-String Scheme: http://openjdk.java.net/jeps/223
+                       StringTokenizer st = new StringTokenizer(System.getProperty("java.version"), "._-+");
+                       int majorVersion = Integer.parseInt(st.nextToken());
+                       isJava9orLater = majorVersion >= 9;
+               } catch (Exception e) {
+                       // Java 8 or older
+               }
+       }
+
+//     /** Cache.
+//      */
+//     private final static IdentityHashMap<FontMetrics, Point.Float> FM_MAP2 = new IdentityHashMap<FontMetrics, Point.Float>(4);
+//     private final static Font SUBST_FONT2 = new Font("sansserif", Font.PLAIN, 11);
+//
+//     public float getDialogUnit(boolean isHor)
+//     {
+//             Font font = c.getFont();
+//             FontMetrics fm = c.getFontMetrics(font != null ? font : SUBST_FONT2);
+//             Point.Float dluP = FM_MAP2.get(fm);
+//             if (dluP == null) {
+//                     float w = fm.charWidth('X') / 4f;
+//                     int ascent = fm.getAscent();
+//                     float h = (ascent > 14 ? ascent : ascent + (15 - ascent) / 3) / 8f;
+//
+//                     dluP = new Point.Float(w, h);
+//                     FM_MAP2.put(fm, dluP);
+//             }
+//             return isHor ? dluP.x : dluP.y;
+//     }
+
+       @Override
+       public final int getX()
+       {
+               return c.getX();
+       }
+
+       @Override
+       public final int getY()
+       {
+               return c.getY();
+       }
+
+       @Override
+       public final int getHeight()
+       {
+               return c.getHeight();
+       }
+
+       @Override
+       public final int getWidth()
+       {
+               return c.getWidth();
+       }
+
+       @Override
+       public final int getScreenLocationX()
+       {
+               Point p = new Point();
+               SwingUtilities.convertPointToScreen(p, c);
+               return p.x;
+       }
+
+       @Override
+       public final int getScreenLocationY()
+       {
+               Point p = new Point();
+               SwingUtilities.convertPointToScreen(p, c);
+               return p.y;
+       }
+
+       @Override
+       public final int getMinimumHeight(int sz)
+       {
+               if (prefCalled == false) {
+                       c.getPreferredSize(); // To defeat a bug where the minimum size is different before and after the first call to getPreferredSize();
+                       prefCalled = true;
+               }
+               return c.getMinimumSize().height;
+       }
+
+       @Override
+       public final int getMinimumWidth(int sz)
+       {
+               if (prefCalled == false) {
+                       c.getPreferredSize(); // To defeat a bug where the minimum size is different before and after the first call to getPreferredSize();
+                       prefCalled = true;
+               }
+               return c.getMinimumSize().width;
+       }
+       @Override
+       public final int getPreferredHeight(int sz)
+       {
+               // If the component has not gotten size yet and there is a size hint, trick Swing to return a better height.
+               if (c.getWidth() == 0 && c.getHeight() == 0 && sz != -1)
+                       c.setBounds(c.getX(), c.getY(), sz, 1);
+
+               return c.getPreferredSize().height;
+       }
+
+       @Override
+       public final int getPreferredWidth(int sz)
+       {
+               // If the component has not gotten size yet and there is a size hint, trick Swing to return a better height.
+               if (c.getWidth() == 0 && c.getHeight() == 0 && sz != -1)
+                       c.setBounds(c.getX(), c.getY(), 1, sz);
+
+               return c.getPreferredSize().width;
+       }
+
+       @Override
+       public final int getMaximumHeight(int sz)
+       {
+               if (!isMaxSet(c))
+                       return Integer.MAX_VALUE;
+
+               return c.getMaximumSize().height;
+       }
+
+       @Override
+       public final int getMaximumWidth(int sz)
+       {
+               if (!isMaxSet(c))
+                       return Integer.MAX_VALUE;
+
+               return c.getMaximumSize().width;
+       }
+
+
+       private boolean isMaxSet(Component c)
+       {
+               return c.isMaximumSizeSet();
+       }
+
+       @Override
+       public final ContainerWrapper getParent()
+       {
+               Container p = c.getParent();
+               return p != null ? new SwingContainerWrapper(p) : null;
+       }
+
+    @Override
+    public final int getHorizontalScreenDPI() {
+        try {
+            return c.getToolkit().getScreenResolution();
+        } catch (HeadlessException ex) {
+            return PlatformDefaults.getDefaultDPI();
+        }
+    }
+
+       @Override
+       public final int getVerticalScreenDPI()
+       {
+        try {
+            return c.getToolkit().getScreenResolution();
+        } catch (HeadlessException ex) {
+            return PlatformDefaults.getDefaultDPI();
+        }
+       }
+
+       @Override
+       public final int getScreenWidth()
+       {
+               try {
+                       return c.getToolkit().getScreenSize().width;
+               } catch (HeadlessException ex) {
+                       return 1024;
+               }
+       }
+
+       @Override
+       public final int getScreenHeight()
+       {
+               try {
+                       return c.getToolkit().getScreenSize().height;
+               } catch (HeadlessException ex) {
+                       return 768;
+               }
+       }
+
+       @Override
+       public final boolean hasBaseline()
+       {
+               if (bl == null) {
+                       try {
+                               // Removed since OTHER is sometimes returned even though there is a valid baseline (e.g. an empty JComboBox)
+//                             if (c.getBaselineResizeBehavior() == Component.BaselineResizeBehavior.OTHER) {
+//                                     bl = Boolean.FALSE;
+//                             } else {
+                                       // Removed since it made some components layout themselves to the minimum size and that stuck after that. E.g. JLabel with HTML content and white spaces would be very tall.
+//                                     Dimension d = c.getPreferredSize();
+//                                     bl = getBaseline(d.width, d.height) > -1;
+                                       bl = getBaseline(8192, 8192) > -1;  // Use large number but don't risk overflow or exposing size bugs with Integer.MAX_VALUE
+//                             }
+                       } catch (Throwable ex) {
+                               bl = Boolean.FALSE;
+                       }
+               }
+               return bl;
+       }
+
+       @Override
+       public final String getLinkId()
+       {
+               return c.getName();
+       }
+
+       @Override
+       public final void setBounds(int x, int y, int width, int height)
+       {
+               c.setBounds(x, y, width, height);
+       }
+
+       @Override
+       public boolean isVisible()
+       {
+               return c.isVisible();
+       }
+
+       @Override
+       public final int[] getVisualPadding()
+       {
+               int[] padding = null;
+               if (isVisualPaddingEnabled()) {
+                       //First try "visualPadding" client property
+                       if (c instanceof JComponent) {
+                               JComponent component = (JComponent) c;
+                               Object padValue = component.getClientProperty(VISUAL_PADDING_PROPERTY);
+
+                               if (padValue instanceof int[] ) {
+                                       //client property value could be an int[]
+                                       padding = (int[]) padValue;
+                               } else if (padValue instanceof Insets) {
+                                       //OR client property value could be an Insets
+                                       Insets padInsets = (Insets) padValue;
+                                       padding = new int[] { padInsets.top, padInsets.left, padInsets.bottom, padInsets.right };
+                               }
+
+                               if (padding == null) {
+                                       //No client property set on the individual JComponent,
+                                       //      so check for a LAF setting for the component type.
+                                       String classID;
+                                       switch (getComponentType(false)) {
+                                               case TYPE_BUTTON:
+                                                       Border border = component.getBorder();
+                                                       if (border != null && border.getClass().getName().startsWith("com.apple.laf.AquaButtonBorder")) {
+                                                               if (PlatformDefaults.getPlatform() == PlatformDefaults.MAC_OSX) {
+                                                                       Object buttonType = component.getClientProperty("JButton.buttonType");
+                                                                       if (buttonType == null) {
+                                                                               classID = component.getHeight() < 33 ? "Button" : "Button.bevel";
+                                                                       } else {
+                                                                               classID = "Button." + buttonType;
+                                                                       }
+                                                                       if (((AbstractButton) component).getIcon() != null)
+                                                                               classID += ".icon";
+                                                               } else {
+                                                                       classID = "Button";
+                                                               }
+                                                       } else {
+                                                               classID = "";
+                                                       }
+                                                       break;
+
+                                               case TYPE_CHECK_BOX:
+                                                       border = component.getBorder();
+                                                       if (border != null && border.getClass().getName().startsWith("com.apple.laf.AquaButtonBorder")) {
+                                                               Object size = component.getClientProperty("JComponent.sizeVariant");
+                                                               if (size != null && size.toString().equals("regular") == false) {
+                                                                       size = "." + size;
+                                                               } else {
+                                                                       size = "";
+                                                               }
+
+                                                               if (component instanceof JRadioButton) {
+                                                                       classID = "RadioButton" + size;
+                                                               } else if (component instanceof JCheckBox) {
+                                                                       classID = "CheckBox" + size;
+                                                               } else {
+                                                                       classID = "ToggleButton" + size;
+                                                               }
+                                                       } else {
+                                                               classID = "";
+                                                       }
+                                                       break;
+
+                                               case TYPE_COMBO_BOX:
+                                                       if (PlatformDefaults.getPlatform() == PlatformDefaults.MAC_OSX) {
+                                                               if (((JComboBox) component).isEditable()) {
+                                                                       Object isSquare = component.getClientProperty("JComboBox.isSquare");
+                                                                       if (isSquare != null && isSquare.toString().equals("true")) {
+                                                                               classID = "ComboBox.editable.isSquare";
+                                                                       } else {
+                                                                               classID = "ComboBox.editable";
+                                                                       }
+
+                                                               } else {
+                                                                       Object isSquare = component.getClientProperty("JComboBox.isSquare");
+                                                                       Object isPopDown = component.getClientProperty("JComboBox.isPopDown");
+
+                                                                       if (isSquare != null && isSquare.toString().equals("true")) {
+                                                                               classID = "ComboBox.isSquare";
+                                                                       } else if (isPopDown != null && isPopDown.toString().equals("true")) {
+                                                                               classID = "ComboBox.isPopDown";
+                                                                       } else {
+                                                                               classID = "ComboBox";
+                                                                       }
+                                                               }
+                                                       } else {
+                                                               classID = "ComboBox";
+                                                       }
+                                                       break;
+                                               case TYPE_CONTAINER:
+                                                       classID = "Container";
+                                                       break;
+                                               case TYPE_IMAGE:
+                                                       classID = "Image";
+                                                       break;
+                                               case TYPE_LABEL:
+                                                       classID = "Label";
+                                                       break;
+                                               case TYPE_LIST:
+                                                       classID = "List";
+                                                       break;
+                                               case TYPE_PANEL:
+                                                       classID = "Panel";
+                                                       break;
+                                               case TYPE_PROGRESS_BAR:
+                                                       classID = "ProgressBar";
+                                                       break;
+                                               case TYPE_SCROLL_BAR:
+                                                       classID = "ScrollBar";
+                                                       break;
+                                               case TYPE_SCROLL_PANE:
+                                                       classID = "ScrollPane";
+                                                       break;
+                                               case TYPE_SEPARATOR:
+                                                       classID = "Separator";
+                                                       break;
+                                               case TYPE_SLIDER:
+                                                       classID = "Slider";
+                                                       break;
+                                               case TYPE_SPINNER:
+                                                       classID = "Spinner";
+                                                       break;
+                                               case TYPE_TABLE:
+                                                       classID = "Table";
+                                                       break;
+                                               case TYPE_TABBED_PANE:
+                                                       classID = "TabbedPane";
+                                                       break;
+                                               case TYPE_TEXT_AREA:
+                                                       classID = "TextArea";
+                                                       break;
+                                               case TYPE_TEXT_FIELD:
+                                                       border = component.getBorder();
+                                                       if (!component.isOpaque() && border != null && border.getClass().getSimpleName().equals("AquaTextFieldBorder")) {
+                                                               classID = "TextField";
+                                                       } else {
+                                                               classID = "";
+                                                       }
+                                                       break;
+                                               case TYPE_TREE:
+                                                       classID = "Tree";
+                                                       break;
+                                               case TYPE_UNKNOWN:
+                                                       classID = "Other";
+                                                       break;
+                                               case TYPE_UNSET:
+                                               default:
+                                                       classID = "";
+                                                       break;
+                                       }
+
+                                       padValue = PlatformDefaults.getDefaultVisualPadding(classID + "." + VISUAL_PADDING_PROPERTY);
+                                       if (padValue instanceof int[]) {
+                                               //client property value could be an int[]
+                                               padding = (int[]) padValue;
+                                       } else if (padValue instanceof Insets) {
+                                               //OR client property value could be an Insets
+                                               Insets padInsets = (Insets) padValue;
+                                               padding = new int[] { padInsets.top, padInsets.left, padInsets.bottom, padInsets.right };
+                                       }
+                               }
+                       }
+               }
+               return padding;
+       }
+
+       /**
+        * @deprecated Java 1.4 is not supported anymore
+        */
+       public static boolean isMaxSizeSetOn1_4()
+       {
+               return maxSet;
+       }
+
+       /**
+        * @deprecated Java 1.4 is not supported anymore
+        */
+       public static void setMaxSizeSetOn1_4(boolean b)
+       {
+               maxSet = b;
+       }
+
+       public static boolean isVisualPaddingEnabled()
+       {
+               return vp;
+       }
+
+       public static void setVisualPaddingEnabled(boolean b)
+       {
+               vp = b;
+       }
+
+       @Override
+       public final void paintDebugOutline(boolean showVisualPadding)
+       {
+               if (c.isShowing() == false)
+                       return;
+
+               Graphics2D g = (Graphics2D) c.getGraphics();
+               if (g == null)
+                       return;
+
+               g.setPaint(DB_COMP_OUTLINE);
+               g.setStroke(new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {2f, 4f}, 0));
+               g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
+
+               if (showVisualPadding && isVisualPaddingEnabled()) {
+                       int[] padding = getVisualPadding();
+                       if (padding != null) {
+                               g.setColor(Color.GREEN);
+                               g.drawRect(padding[1], padding[0], (getWidth() - 1) - (padding[1] + padding[3]), (getHeight() - 1) - (padding[0] + padding[2]));
+                       }
+               }
+       }
+
+       @Override
+       public int getComponentType(boolean disregardScrollPane)
+       {
+               if (compType == TYPE_UNSET)
+                       compType = checkType(disregardScrollPane);
+
+               return compType;
+       }
+
+       @Override
+       public int getLayoutHashCode()
+       {
+               Dimension d = c.getMaximumSize();
+               int hash = d.width + (d.height << 5);
+
+               d = c.getPreferredSize();
+               hash += (d.width << 10) + (d.height << 15);
+
+               d = c.getMinimumSize();
+               hash += (d.width << 20) + (d.height << 25);
+
+               if (c.isVisible())
+                       hash += 1324511;
+
+               String id = getLinkId();
+               if (id != null)
+                       hash += id.hashCode();
+
+               return hash;
+       }
+
+       private int checkType(boolean disregardScrollPane)
+       {
+               Component c = this.c;
+
+               if (disregardScrollPane) {
+                       if (c instanceof JScrollPane) {
+                               c = ((JScrollPane) c).getViewport().getView();
+                       } else if (c instanceof ScrollPane) {
+                               c = ((ScrollPane) c).getComponent(0);
+                       }
+               }
+
+               if (c instanceof JTextField || c instanceof TextField) {
+                       return TYPE_TEXT_FIELD;
+               } else if (c instanceof JLabel || c instanceof Label) {
+                       return TYPE_LABEL;
+               } else if (c instanceof JCheckBox || c instanceof JRadioButton || c instanceof Checkbox) {
+                       return TYPE_CHECK_BOX;
+               } else if (c instanceof AbstractButton || c instanceof Button) {
+                       return TYPE_BUTTON;
+               } else if (c instanceof JComboBox || c instanceof Choice) {
+                       return TYPE_COMBO_BOX;
+               } else if (c instanceof JTextComponent || c instanceof TextComponent) {
+                       return TYPE_TEXT_AREA;
+               } else if (c instanceof JPanel || c instanceof Canvas) {
+                       return TYPE_PANEL;
+               } else if (c instanceof JList || c instanceof List) {
+                       return TYPE_LIST;
+               } else if (c instanceof JTable) {
+                       return TYPE_TABLE;
+               } else if (c instanceof JSeparator) {
+                       return TYPE_SEPARATOR;
+               } else if (c instanceof JSpinner) {
+                       return TYPE_SPINNER;
+               } else if (c instanceof JTabbedPane) {
+                       return TYPE_TABBED_PANE;
+               } else if (c instanceof JProgressBar) {
+                       return TYPE_PROGRESS_BAR;
+               } else if (c instanceof JSlider) {
+                       return TYPE_SLIDER;
+               } else if (c instanceof JScrollPane) {
+                       return TYPE_SCROLL_PANE;
+               } else if (c instanceof JScrollBar || c instanceof Scrollbar) {
+                       return TYPE_SCROLL_BAR;
+               } else if (c instanceof Container) {    // only AWT components is not containers.
+                       return TYPE_CONTAINER;
+               }
+               return TYPE_UNKNOWN;
+       }
+
+       @Override
+       public final int hashCode()
+       {
+               return getComponent().hashCode();
+       }
+
+       @Override
+       public final boolean equals(Object o)
+       {
+               if (o instanceof ComponentWrapper == false)
+                       return false;
+
+               return c.equals(((ComponentWrapper) o).getComponent());
+       }
+
+       @Override
+       public int getContentBias()
+       {
+               return c instanceof JTextArea || c instanceof JEditorPane || (c instanceof JComponent && Boolean.TRUE.equals(((JComponent)c).getClientProperty("migLayout.dynamicAspectRatio"))) ? LayoutUtil.HORIZONTAL : -1;
+       }
+}
diff --git a/srcjar/net/miginfocom/swing/SwingContainerWrapper.java b/srcjar/net/miginfocom/swing/SwingContainerWrapper.java
new file mode 100644 (file)
index 0000000..ae352ba
--- /dev/null
@@ -0,0 +1,120 @@
+package net.miginfocom.swing;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import net.miginfocom.layout.ComponentWrapper;
+import net.miginfocom.layout.ContainerWrapper;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics2D;
+
+/**
+ */
+public final class SwingContainerWrapper extends SwingComponentWrapper implements ContainerWrapper
+{
+       /** Debug color for cell outline.
+        */
+       private static final Color DB_CELL_OUTLINE = new Color(255, 0, 0);
+
+       public SwingContainerWrapper(Container c)
+       {
+               super(c);
+       }
+
+       @Override
+       public ComponentWrapper[] getComponents()
+       {
+               Container c = (Container) getComponent();
+               ComponentWrapper[] cws = new ComponentWrapper[c.getComponentCount()];
+               for (int i = 0; i < cws.length; i++)
+                       cws[i] = new SwingComponentWrapper(c.getComponent(i));
+               return cws;
+       }
+
+       @Override
+       public int getComponentCount()
+       {
+               return ((Container) getComponent()).getComponentCount();
+       }
+
+       @Override
+       public Object getLayout()
+       {
+               return ((Container) getComponent()).getLayout();
+       }
+
+       @Override
+       public final boolean isLeftToRight()
+       {
+               return ((Container) getComponent()).getComponentOrientation().isLeftToRight();
+       }
+
+       @Override
+       public final void paintDebugCell(int x, int y, int width, int height)
+       {
+               Component c = (Component) getComponent();
+               if (c.isShowing() == false)
+                       return;
+
+               Graphics2D g = (Graphics2D) c.getGraphics();
+               if (g == null)
+                       return;
+
+               g.setStroke(new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {2f, 3f}, 0));
+               g.setPaint(DB_CELL_OUTLINE);
+               g.drawRect(x, y, width - 1, height - 1);
+       }
+
+       @Override
+       public int getComponentType(boolean disregardScrollPane)
+       {
+               return TYPE_CONTAINER;
+       }
+
+       // Removed for 2.3 because the parent.isValid() in MigLayout will catch this instead.
+       @Override
+       public int getLayoutHashCode()
+       {
+               long n = System.nanoTime();
+               int h = super.getLayoutHashCode();
+
+               if (isLeftToRight())
+                       h += 416343;
+
+               return 0;
+       }
+}