--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/Spring.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,691 @@
+/*
+ * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package javax.swing;
+
+import java.awt.Component;
+
+/**
+ * An instance of the <code>Spring</code> class holds three properties that
+ * characterize its behavior: the <em>minimum</em>, <em>preferred</em>, and
+ * <em>maximum</em> values. Each of these properties may be involved in
+ * defining its fourth, <em>value</em>, property based on a series of rules.
+ * <p>
+ * An instance of the <code>Spring</code> class can be visualized as a
+ * mechanical spring that provides a corrective force as the spring is compressed
+ * or stretched away from its preferred value. This force is modelled
+ * as linear function of the distance from the preferred value, but with
+ * two different constants -- one for the compressional force and one for the
+ * tensional one. Those constants are specified by the minimum and maximum
+ * values of the spring such that a spring at its minimum value produces an
+ * equal and opposite force to that which is created when it is at its
+ * maximum value. The difference between the <em>preferred</em> and
+ * <em>minimum</em> values, therefore, represents the ease with which the
+ * spring can be compressed and the difference between its <em>maximum</em>
+ * and <em>preferred</em> values, indicates the ease with which the
+ * <code>Spring</code> can be extended.
+ * See the {@link #sum} method for details.
+ *
+ * <p>
+ * By defining simple arithmetic operations on <code>Spring</code>s,
+ * the behavior of a collection of <code>Spring</code>s
+ * can be reduced to that of an ordinary (non-compound) <code>Spring</code>. We define
+ * the "+", "-", <em>max</em>, and <em>min</em> operators on
+ * <code>Spring</code>s so that, in each case, the result is a <code>Spring</code>
+ * whose characteristics bear a useful mathematical relationship to its constituent
+ * springs.
+ *
+ * <p>
+ * A <code>Spring</code> can be treated as a pair of intervals
+ * with a single common point: the preferred value.
+ * The following rules define some of the
+ * arithmetic operators that can be applied to intervals
+ * (<code>[a, b]</code> refers to the interval
+ * from <code>a</code>
+ * to <code>b</code>,
+ * where <code>a <= b</code>).
+ * <p>
+ * <pre>
+ * [a1, b1] + [a2, b2] = [a1 + a2, b1 + b2]
+ *
+ * -[a, b] = [-b, -a]
+ *
+ * max([a1, b1], [a2, b2]) = [max(a1, a2), max(b1, b2)]
+ * </pre>
+ * <p>
+ *
+ * If we denote <code>Spring</code>s as <code>[a, b, c]</code>,
+ * where <code>a <= b <= c</code>, we can define the same
+ * arithmetic operators on <code>Spring</code>s:
+ * <p>
+ * <pre>
+ * [a1, b1, c1] + [a2, b2, c2] = [a1 + a2, b1 + b2, c1 + c2]
+ *
+ * -[a, b, c] = [-c, -b, -a]
+ *
+ * max([a1, b1, c1], [a2, b2, c2]) = [max(a1, a2), max(b1, b2), max(c1, c2)]
+ * </pre>
+ * <p>
+ * With both intervals and <code>Spring</code>s we can define "-" and <em>min</em>
+ * in terms of negation:
+ * <p>
+ * <pre>
+ * X - Y = X + (-Y)
+ *
+ * min(X, Y) = -max(-X, -Y)
+ * </pre>
+ * <p>
+ * For the static methods in this class that embody the arithmetic
+ * operators, we do not actually perform the operation in question as
+ * that would snapshot the values of the properties of the method's arguments
+ * at the time the static method is called. Instead, the static methods
+ * create a new <code>Spring</code> instance containing references to
+ * the method's arguments so that the characteristics of the new spring track the
+ * potentially changing characteristics of the springs from which it
+ * was made. This is a little like the idea of a <em>lazy value</em>
+ * in a functional language.
+ * <p>
+ * If you are implementing a <code>SpringLayout</code> you
+ * can find further information and examples in
+ * <a
+ href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a>,
+ * a section in <em>The Java Tutorial.</em>
+ * <p>
+ * <strong>Warning:</strong>
+ * Serialized objects of this class will not be compatible with
+ * future Swing releases. The current serialization support is
+ * appropriate for short term storage or RMI between applications running
+ * the same version of Swing. As of 1.4, support for long term storage
+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
+ * has been added to the <code>java.beans</code> package.
+ * Please see {@link java.beans.XMLEncoder}.
+ *
+ * @see SpringLayout
+ * @see SpringLayout.Constraints
+ *
+ * @author Philip Milne
+ * @since 1.4
+ */
+public abstract class Spring {
+
+ /**
+ * An integer value signifying that a property value has not yet been calculated.
+ */
+ public static final int UNSET = Integer.MIN_VALUE;
+
+ /**
+ * Used by factory methods to create a <code>Spring</code>.
+ *
+ * @see #constant(int)
+ * @see #constant(int, int, int)
+ * @see #max
+ * @see #minus
+ * @see #sum
+ * @see SpringLayout.Constraints
+ */
+ protected Spring() {}
+
+ /**
+ * Returns the <em>minimum</em> value of this <code>Spring</code>.
+ *
+ * @return the <code>minimumValue</code> property of this <code>Spring</code>
+ */
+ public abstract int getMinimumValue();
+
+ /**
+ * Returns the <em>preferred</em> value of this <code>Spring</code>.
+ *
+ * @return the <code>preferredValue</code> of this <code>Spring</code>
+ */
+ public abstract int getPreferredValue();
+
+ /**
+ * Returns the <em>maximum</em> value of this <code>Spring</code>.
+ *
+ * @return the <code>maximumValue</code> property of this <code>Spring</code>
+ */
+ public abstract int getMaximumValue();
+
+ /**
+ * Returns the current <em>value</em> of this <code>Spring</code>.
+ *
+ * @return the <code>value</code> property of this <code>Spring</code>
+ *
+ * @see #setValue
+ */
+ public abstract int getValue();
+
+ /**
+ * Sets the current <em>value</em> of this <code>Spring</code> to <code>value</code>.
+ *
+ * @param value the new setting of the <code>value</code> property
+ *
+ * @see #getValue
+ */
+ public abstract void setValue(int value);
+
+ private double range(boolean contract) {
+ return contract ? (getPreferredValue() - getMinimumValue()) :
+ (getMaximumValue() - getPreferredValue());
+ }
+
+ /*pp*/ double getStrain() {
+ double delta = (getValue() - getPreferredValue());
+ return delta/range(getValue() < getPreferredValue());
+ }
+
+ /*pp*/ void setStrain(double strain) {
+ setValue(getPreferredValue() + (int)(strain * range(strain < 0)));
+ }
+
+ /*pp*/ boolean isCyclic(SpringLayout l) {
+ return false;
+ }
+
+ /*pp*/ static abstract class AbstractSpring extends Spring {
+ protected int size = UNSET;
+
+ public int getValue() {
+ return size != UNSET ? size : getPreferredValue();
+ }
+
+ public final void setValue(int size) {
+ if (this.size == size) {
+ return;
+ }
+ if (size == UNSET) {
+ clear();
+ } else {
+ setNonClearValue(size);
+ }
+ }
+
+ protected void clear() {
+ size = UNSET;
+ }
+
+ protected void setNonClearValue(int size) {
+ this.size = size;
+ }
+ }
+
+ private static class StaticSpring extends AbstractSpring {
+ protected int min;
+ protected int pref;
+ protected int max;
+
+ public StaticSpring(int pref) {
+ this(pref, pref, pref);
+ }
+
+ public StaticSpring(int min, int pref, int max) {
+ this.min = min;
+ this.pref = pref;
+ this.max = max;
+ }
+
+ public String toString() {
+ return "StaticSpring [" + min + ", " + pref + ", " + max + "]";
+ }
+
+ public int getMinimumValue() {
+ return min;
+ }
+
+ public int getPreferredValue() {
+ return pref;
+ }
+
+ public int getMaximumValue() {
+ return max;
+ }
+ }
+
+ private static class NegativeSpring extends Spring {
+ private Spring s;
+
+ public NegativeSpring(Spring s) {
+ this.s = s;
+ }
+
+// Note the use of max value rather than minimum value here.
+// See the opening preamble on arithmetic with springs.
+
+ public int getMinimumValue() {
+ return -s.getMaximumValue();
+ }
+
+ public int getPreferredValue() {
+ return -s.getPreferredValue();
+ }
+
+ public int getMaximumValue() {
+ return -s.getMinimumValue();
+ }
+
+ public int getValue() {
+ return -s.getValue();
+ }
+
+ public void setValue(int size) {
+ // No need to check for UNSET as
+ // Integer.MIN_VALUE == -Integer.MIN_VALUE.
+ s.setValue(-size);
+ }
+
+ /*pp*/ boolean isCyclic(SpringLayout l) {
+ return s.isCyclic(l);
+ }
+ }
+
+ private static class ScaleSpring extends Spring {
+ private Spring s;
+ private float factor;
+
+ private ScaleSpring(Spring s, float factor) {
+ this.s = s;
+ this.factor = factor;
+ }
+
+ public int getMinimumValue() {
+ return Math.round((factor < 0 ? s.getMaximumValue() : s.getMinimumValue()) * factor);
+ }
+
+ public int getPreferredValue() {
+ return Math.round(s.getPreferredValue() * factor);
+ }
+
+ public int getMaximumValue() {
+ return Math.round((factor < 0 ? s.getMinimumValue() : s.getMaximumValue()) * factor);
+ }
+
+ public int getValue() {
+ return Math.round(s.getValue() * factor);
+ }
+
+ public void setValue(int value) {
+ if (value == UNSET) {
+ s.setValue(UNSET);
+ } else {
+ s.setValue(Math.round(value / factor));
+ }
+ }
+
+ /*pp*/ boolean isCyclic(SpringLayout l) {
+ return s.isCyclic(l);
+ }
+ }
+
+ /*pp*/ static class WidthSpring extends AbstractSpring {
+ /*pp*/ Component c;
+
+ public WidthSpring(Component c) {
+ this.c = c;
+ }
+
+ public int getMinimumValue() {
+ return c.getMinimumSize().width;
+ }
+
+ public int getPreferredValue() {
+ return c.getPreferredSize().width;
+ }
+
+ public int getMaximumValue() {
+ // We will be doing arithmetic with the results of this call,
+ // so if a returned value is Integer.MAX_VALUE we will get
+ // arithmetic overflow. Truncate such values.
+ return Math.min(Short.MAX_VALUE, c.getMaximumSize().width);
+ }
+ }
+
+ /*pp*/ static class HeightSpring extends AbstractSpring {
+ /*pp*/ Component c;
+
+ public HeightSpring(Component c) {
+ this.c = c;
+ }
+
+ public int getMinimumValue() {
+ return c.getMinimumSize().height;
+ }
+
+ public int getPreferredValue() {
+ return c.getPreferredSize().height;
+ }
+
+ public int getMaximumValue() {
+ return Math.min(Short.MAX_VALUE, c.getMaximumSize().height);
+ }
+ }
+
+ /*pp*/ static abstract class SpringMap extends Spring {
+ private Spring s;
+
+ public SpringMap(Spring s) {
+ this.s = s;
+ }
+
+ protected abstract int map(int i);
+
+ protected abstract int inv(int i);
+
+ public int getMinimumValue() {
+ return map(s.getMinimumValue());
+ }
+
+ public int getPreferredValue() {
+ return map(s.getPreferredValue());
+ }
+
+ public int getMaximumValue() {
+ return Math.min(Short.MAX_VALUE, map(s.getMaximumValue()));
+ }
+
+ public int getValue() {
+ return map(s.getValue());
+ }
+
+ public void setValue(int value) {
+ if (value == UNSET) {
+ s.setValue(UNSET);
+ } else {
+ s.setValue(inv(value));
+ }
+ }
+
+ /*pp*/ boolean isCyclic(SpringLayout l) {
+ return s.isCyclic(l);
+ }
+ }
+
+// Use the instance variables of the StaticSpring superclass to
+// cache values that have already been calculated.
+ /*pp*/ static abstract class CompoundSpring extends StaticSpring {
+ protected Spring s1;
+ protected Spring s2;
+
+ public CompoundSpring(Spring s1, Spring s2) {
+ super(UNSET);
+ this.s1 = s1;
+ this.s2 = s2;
+ }
+
+ public String toString() {
+ return "CompoundSpring of " + s1 + " and " + s2;
+ }
+
+ protected void clear() {
+ super.clear();
+ min = pref = max = UNSET;
+ s1.setValue(UNSET);
+ s2.setValue(UNSET);
+ }
+
+ protected abstract int op(int x, int y);
+
+ public int getMinimumValue() {
+ if (min == UNSET) {
+ min = op(s1.getMinimumValue(), s2.getMinimumValue());
+ }
+ return min;
+ }
+
+ public int getPreferredValue() {
+ if (pref == UNSET) {
+ pref = op(s1.getPreferredValue(), s2.getPreferredValue());
+ }
+ return pref;
+ }
+
+ public int getMaximumValue() {
+ if (max == UNSET) {
+ max = op(s1.getMaximumValue(), s2.getMaximumValue());
+ }
+ return max;
+ }
+
+ public int getValue() {
+ if (size == UNSET) {
+ size = op(s1.getValue(), s2.getValue());
+ }
+ return size;
+ }
+
+ /*pp*/ boolean isCyclic(SpringLayout l) {
+ return l.isCyclic(s1) || l.isCyclic(s2);
+ }
+ };
+
+ private static class SumSpring extends CompoundSpring {
+ public SumSpring(Spring s1, Spring s2) {
+ super(s1, s2);
+ }
+
+ protected int op(int x, int y) {
+ return x + y;
+ }
+
+ protected void setNonClearValue(int size) {
+ super.setNonClearValue(size);
+ s1.setStrain(this.getStrain());
+ s2.setValue(size - s1.getValue());
+ }
+ }
+
+ private static class MaxSpring extends CompoundSpring {
+
+ public MaxSpring(Spring s1, Spring s2) {
+ super(s1, s2);
+ }
+
+ protected int op(int x, int y) {
+ return Math.max(x, y);
+ }
+
+ protected void setNonClearValue(int size) {
+ super.setNonClearValue(size);
+ s1.setValue(size);
+ s2.setValue(size);
+ }
+ }
+
+ /**
+ * Returns a strut -- a spring whose <em>minimum</em>, <em>preferred</em>, and
+ * <em>maximum</em> values each have the value <code>pref</code>.
+ *
+ * @param pref the <em>minimum</em>, <em>preferred</em>, and
+ * <em>maximum</em> values of the new spring
+ * @return a spring whose <em>minimum</em>, <em>preferred</em>, and
+ * <em>maximum</em> values each have the value <code>pref</code>
+ *
+ * @see Spring
+ */
+ public static Spring constant(int pref) {
+ return constant(pref, pref, pref);
+ }
+
+ /**
+ * Returns a spring whose <em>minimum</em>, <em>preferred</em>, and
+ * <em>maximum</em> values have the values: <code>min</code>, <code>pref</code>,
+ * and <code>max</code> respectively.
+ *
+ * @param min the <em>minimum</em> value of the new spring
+ * @param pref the <em>preferred</em> value of the new spring
+ * @param max the <em>maximum</em> value of the new spring
+ * @return a spring whose <em>minimum</em>, <em>preferred</em>, and
+ * <em>maximum</em> values have the values: <code>min</code>, <code>pref</code>,
+ * and <code>max</code> respectively
+ *
+ * @see Spring
+ */
+ public static Spring constant(int min, int pref, int max) {
+ return new StaticSpring(min, pref, max);
+ }
+
+
+ /**
+ * Returns <code>-s</code>: a spring running in the opposite direction to <code>s</code>.
+ *
+ * @return <code>-s</code>: a spring running in the opposite direction to <code>s</code>
+ *
+ * @see Spring
+ */
+ public static Spring minus(Spring s) {
+ return new NegativeSpring(s);
+ }
+
+ /**
+ * Returns <code>s1+s2</code>: a spring representing <code>s1</code> and <code>s2</code>
+ * in series. In a sum, <code>s3</code>, of two springs, <code>s1</code> and <code>s2</code>,
+ * the <em>strains</em> of <code>s1</code>, <code>s2</code>, and <code>s3</code> are maintained
+ * at the same level (to within the precision implied by their integer <em>value</em>s).
+ * The strain of a spring in compression is:
+ * <pre>
+ * value - pref
+ * ------------
+ * pref - min
+ * </pre>
+ * and the strain of a spring in tension is:
+ * <pre>
+ * value - pref
+ * ------------
+ * max - pref
+ * </pre>
+ * When <code>setValue</code> is called on the sum spring, <code>s3</code>, the strain
+ * in <code>s3</code> is calculated using one of the formulas above. Once the strain of
+ * the sum is known, the <em>value</em>s of <code>s1</code> and <code>s2</code> are
+ * then set so that they are have a strain equal to that of the sum. The formulas are
+ * evaluated so as to take rounding errors into account and ensure that the sum of
+ * the <em>value</em>s of <code>s1</code> and <code>s2</code> is exactly equal to
+ * the <em>value</em> of <code>s3</code>.
+ *
+ * @return <code>s1+s2</code>: a spring representing <code>s1</code> and <code>s2</code> in series
+ *
+ * @see Spring
+ */
+ public static Spring sum(Spring s1, Spring s2) {
+ return new SumSpring(s1, s2);
+ }
+
+ /**
+ * Returns <code>max(s1, s2)</code>: a spring whose value is always greater than (or equal to)
+ * the values of both <code>s1</code> and <code>s2</code>.
+ *
+ * @return <code>max(s1, s2)</code>: a spring whose value is always greater than (or equal to)
+ * the values of both <code>s1</code> and <code>s2</code>
+ * @see Spring
+ */
+ public static Spring max(Spring s1, Spring s2) {
+ return new MaxSpring(s1, s2);
+ }
+
+ // Remove these, they're not used often and can be created using minus -
+ // as per these implementations.
+
+ /*pp*/ static Spring difference(Spring s1, Spring s2) {
+ return sum(s1, minus(s2));
+ }
+
+ /*
+ public static Spring min(Spring s1, Spring s2) {
+ return minus(max(minus(s1), minus(s2)));
+ }
+ */
+
+ /**
+ * Returns a spring whose <em>minimum</em>, <em>preferred</em>, <em>maximum</em>
+ * and <em>value</em> properties are each multiples of the properties of the
+ * argument spring, <code>s</code>. Minimum and maximum properties are
+ * swapped when <code>factor</code> is negative (in accordance with the
+ * rules of interval arithmetic).
+ * <p>
+ * When factor is, for example, 0.5f the result represents 'the mid-point'
+ * of its input - an operation that is useful for centering components in
+ * a container.
+ *
+ * @param s the spring to scale
+ * @param factor amount to scale by.
+ * @return a spring whose properties are those of the input spring <code>s</code>
+ * multiplied by <code>factor</code>
+ * @throws NullPointerException if <code>s</code> is null
+ * @since 1.5
+ */
+ public static Spring scale(Spring s, float factor) {
+ checkArg(s);
+ return new ScaleSpring(s, factor);
+ }
+
+ /**
+ * Returns a spring whose <em>minimum</em>, <em>preferred</em>, <em>maximum</em>
+ * and <em>value</em> properties are defined by the widths of the <em>minimumSize</em>,
+ * <em>preferredSize</em>, <em>maximumSize</em> and <em>size</em> properties
+ * of the supplied component. The returned spring is a 'wrapper' implementation
+ * whose methods call the appropriate size methods of the supplied component.
+ * The minimum, preferred, maximum and value properties of the returned spring
+ * therefore report the current state of the appropriate properties in the
+ * component and track them as they change.
+ *
+ * @param c Component used for calculating size
+ * @return a spring whose properties are defined by the horizontal component
+ * of the component's size methods.
+ * @throws NullPointerException if <code>c</code> is null
+ * @since 1.5
+ */
+ public static Spring width(Component c) {
+ checkArg(c);
+ return new WidthSpring(c);
+ }
+
+ /**
+ * Returns a spring whose <em>minimum</em>, <em>preferred</em>, <em>maximum</em>
+ * and <em>value</em> properties are defined by the heights of the <em>minimumSize</em>,
+ * <em>preferredSize</em>, <em>maximumSize</em> and <em>size</em> properties
+ * of the supplied component. The returned spring is a 'wrapper' implementation
+ * whose methods call the appropriate size methods of the supplied component.
+ * The minimum, preferred, maximum and value properties of the returned spring
+ * therefore report the current state of the appropriate properties in the
+ * component and track them as they change.
+ *
+ * @param c Component used for calculating size
+ * @return a spring whose properties are defined by the vertical component
+ * of the component's size methods.
+ * @throws NullPointerException if <code>c</code> is null
+ * @since 1.5
+ */
+ public static Spring height(Component c) {
+ checkArg(c);
+ return new HeightSpring(c);
+ }
+
+
+ /**
+ * If <code>s</code> is null, this throws an NullPointerException.
+ */
+ private static void checkArg(Object s) {
+ if (s == null) {
+ throw new NullPointerException("Argument must not be null");
+ }
+ }
+}