--- /dev/null
+https://github.com/mikaelgrev/miglayout.git
+2018.07.03 BH Latest commit f2a231c
+
+
+Status
+======
+
+7/3/2018
+
+no changes necessary
--- /dev/null
+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
--- /dev/null
+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 > 0.
+ * @param durMillis Duration in milliseconds. <=0 means default value should be used and > 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 > 0.
+ */
+ public int getPriority()
+ {
+ return prio;
+ }
+
+ /**
+ * @param defMillis Default used if the millis in the spec is set to "default".
+ * @return Duration in milliseconds. <=0 means default value should be used and > 0 is the number of millis
+ */
+ public int getDurationMillis(int defMillis)
+ {
+ return durMillis > 0 ? durMillis : defMillis;
+ }
+
+ /**
+ * @return Duration in milliseconds. <= 0 means default value should be used and > 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;
+ }
+}
--- /dev/null
+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));
+ }
+}
--- /dev/null
+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 >= 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 >= 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
--- /dev/null
+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
--- /dev/null
+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;
+// }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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 > 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 > 0 OR unspecified.
+ * <li>2 - Grow all rows/columns that have a grow weight > 0.
+ * <li>3 - Grow all rows/columns that have a grow weight > 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;
+ }
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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>> 0</code> the debug decorations will be repainted every <code>millis</code>. No debug information if <code><= 0</code> (default).
+ * @return The current debug repaint interval.
+ */
+ public final int getDebugMillis()
+ {
+ return debugMillis;
+ }
+
+ /** If <code>> 0</code> the debug decorations will be repainted every <code>millis</code>. No debug information if <code><= 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);
+ }
+}
--- /dev/null
+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)
+ {
+ }
+}
--- /dev/null
+package net.miginfocom.layout;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+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 > 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 > 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 false;//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 < 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 f 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;
+ return null;
+ }
+//
+// 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)
+ {
+ return null;
+// synchronized(SER_MAP) {
+// return SER_MAP.remove(caller);
+// }
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+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()
+ {
+ if (/** @j2sNative true ||*/false)
+ return WINDOWS_XP;
+ 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&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 >" or "forward >" button.
+ * <li><code>'B'</code> - A tag for the "< back" or "< 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;
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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));
+ }
+}
--- /dev/null
+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
--- /dev/null
+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 = true;
+// 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)
+ {
+ System.out.println("SCW " + c.getName() + " " + c.getClass().getName() + " " + c.getMinimumSize() + " " + c.getPreferredSize() + " " + c.getMinimumSize());
+ 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;
+ }
+}
--- /dev/null
+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;
+ }
+}