diff options
Diffstat (limited to 'prototypes/miglayout/net/miginfocom/layout/LayoutUtil.java')
-rw-r--r-- | prototypes/miglayout/net/miginfocom/layout/LayoutUtil.java | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/prototypes/miglayout/net/miginfocom/layout/LayoutUtil.java b/prototypes/miglayout/net/miginfocom/layout/LayoutUtil.java new file mode 100644 index 00000000..05d0633a --- /dev/null +++ b/prototypes/miglayout/net/miginfocom/layout/LayoutUtil.java @@ -0,0 +1,532 @@ +package net.miginfocom.layout;
+
+import java.beans.*;
+import java.io.*;
+import java.util.IdentityHashMap;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ * Date: 2006-sep-08
+ */
+
+/** A utility class that has only static helper methods.
+ */
+public final class LayoutUtil
+{
+ /** A substitute value for aa 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 unresonable yet it is hard to
+ * overflow.
+ */
+ static final int INF = (Integer.MAX_VALUE >> 10) - 100; // To reduce likelyhood of overflow errors when calculating.
+
+ /** Tag int for a value that in considered "not set". Used as "null" element in int arays.
+ */
+ 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;
+
+ private static WeakHashMap<Object, String> CR_MAP = null;
+ private static WeakHashMap<Object, Boolean> DT_MAP = null; // The Containers that have design time. Value not used.
+ private static int eSz = 15;
+
+ private LayoutUtil()
+ {
+ }
+
+ /** Returns the current version of MiG Layout.
+ * @return The current version of MiG Layout.
+ */
+ public static String getVersion()
+ {
+ return "3.0.3";
+ }
+
+ /** 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), new Boolean(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 Beans.isDesignTime();
+
+ Boolean b = DT_MAP.get(cw != null ? cw.getComponent() : null);
+ return b != null ? b.booleanValue() : false;
+ }
+
+ /** 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 15.
+ */
+ 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 delagates to be used for a class.
+ * @param c The class to set the registered deligate for.
+ * @param del The new deligate or <code>null</code> to erase to old one.
+ */
+ static synchronized void setDelegate(Class c, PersistenceDelegate del)
+ {
+ try {
+ Introspector.getBeanInfo(c).getBeanDescriptor().setValue("persistenceDelegate", del);
+ } catch (Exception e1) {
+ }
+ }
+
+ /** 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 constraitn 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 om the pref/max sizes and resise 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 defGrowWeights If there is no grow weight for a resConstr the correspinding 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[] defGrowWeights, 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(new Integer(isGrow ? resC.growPrio : resC.shrinkPrio));
+ }
+ Integer[] prioIntegers = prioList.toArray(new Integer[prioList.size()]);
+
+ for (int force = 0; force <= ((isGrow && defGrowWeights != 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].intValue();
+
+ 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 : (defGrowWeights[i < defGrowWeights.length ? i : defGrowWeights.length - 1]);
+ } else {
+ resizeWeight[i] = resC.shrink;
+ }
+ if (resizeWeight[i] != null)
+ totWeight += resizeWeight[i].floatValue();
+ }
+ }
+ }
+
+ 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.floatValue() / 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.floatValue();
+ 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> fo no boundary).
+ * @param upper The upper boundary (or <code>null</code> fo no boundary).
+ * @return The broken boundary or <code>null</code> if no boundary was broken.
+ */
+ private static int getBrokenBoundary(float sz, int lower, int upper)
+ {
+ if (lower != NOT_SET) {
+ if (sz < lower)
+ return new Integer(lower);
+ } else if (sz < 0f) {
+ return new Integer(0);
+ }
+
+ if (upper != NOT_SET && sz > upper)
+ return new Integer(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);
+ }
+
+ 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)
+ 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(), 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 ffc does not specify this.
+ * @return If left-to-right orientation is currently used.
+ */
+ public final static boolean isLeftToRight(LC lc, ContainerWrapper container)
+ {
+ if (lc != null && lc.getLeftToRight() != null)
+ return lc.getLeftToRight().booleanValue();
+
+ 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 final 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;
+// }
+
+ /** Converts a <code>float</code> to a string and is removing the ".0" if the float is an integer.
+ * @param f the float.
+ * @return <code>f</code> as a string. Never <code>null</code>.
+ */
+ static final String floatToString(float f)
+ {
+ String valS = String.valueOf(f);
+ return valS.endsWith(".0") ? valS.substring(0, valS.length() - 2) : valS;
+ }
+
+ /** Returns the inset 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 inset for the side. Never <code>null</code>.
+ */
+ static final 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 objet 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 recieve the exeptions 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 boject 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() {
+ public void exceptionThrown(Exception e) {
+ e.printStackTrace();
+ }});
+
+ byte[] buf = writeOutputStream.toByteArray();
+
+ out.writeInt(buf.length);
+ out.write(buf);
+ }
+
+ private static byte[] readBuf = null;
+ /** Reads an object from <code>in</code> using the
+ * @param in The object input to read from.
+ * @return The object. Never <code>null</code>.
+ * @throws IOException If there was a problem saving as XML
+ */
+ public static synchronized Object readAsXML(ObjectInput in) throws IOException
+ {
+ if (readBuf == null)
+ readBuf = new byte[16384];
+
+ Thread cThread = Thread.currentThread();
+ ClassLoader oldCL = null;
+
+ try {
+ oldCL = cThread.getContextClassLoader();
+ cThread.setContextClassLoader(LayoutUtil.class.getClassLoader());
+ } catch(SecurityException e) {
+ }
+
+ 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 e) {
+ }
+
+ if (oldCL != null)
+ cThread.setContextClassLoader(oldCL);
+
+ return o;
+ }
+
+ private static final IdentityHashMap<Object, Object> SER_MAP = new IdentityHashMap<Object, Object>(2);
+
+ /** Sets the serialized object and associates it with <code>caller</code>.
+ * @param caller The objec created <code>o</code>
+ * @param o The just serialized object.
+ */
+ public static void setSerializedObject(Object caller, Object o)
+ {
+ synchronized(SER_MAP) {
+ SER_MAP.put(caller, o);
+ }
+ }
+
+ /** Returns the serialized object that are associated with <code>caller</code>. It also removes it from the list.
+ * @param caller The original creator of the object.
+ * @return The object.
+ */
+ public static Object getSerializedObject(Object caller)
+ {
+ synchronized(SER_MAP) {
+ return SER_MAP.remove(caller);
+ }
+ }
+}
|