1233 lines · 44.0 KB · Java
RawDownload
/* * Copyright (c) 2001, 2008, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing; import java.awt.Component;import java.awt.Container;import java.awt.Dimension;import java.awt.FontMetrics;import java.awt.Insets;import java.awt.LayoutManager2;import java.awt.Rectangle;import java.util.*; /** * A <code>SpringLayout</code> lays out the children of its associated container * according to a set of constraints. * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a> * in <em>The Java Tutorial</em> for examples of using * <code>SpringLayout</code>. * * <p> * Each constraint, * represented by a <code>Spring</code> object, * controls the vertical or horizontal distance * between two component edges. * The edges can belong to * any child of the container, * or to the container itself. * For example, * the allowable width of a component * can be expressed using a constraint * that controls the distance between the west (left) and east (right) * edges of the component. * The allowable <em>y</em> coordinates for a component * can be expressed by constraining the distance between * the north (top) edge of the component * and the north edge of its container. * * <P> * Every child of a <code>SpringLayout</code>-controlled container, * as well as the container itself, * has exactly one set of constraints * associated with it. * These constraints are represented by * a <code>SpringLayout.Constraints</code> object. * By default, * <code>SpringLayout</code> creates constraints * that make their associated component * have the minimum, preferred, and maximum sizes * returned by the component's * {@link java.awt.Component#getMinimumSize}, * {@link java.awt.Component#getPreferredSize}, and * {@link java.awt.Component#getMaximumSize} * methods. The <em>x</em> and <em>y</em> positions are initially not * constrained, so that until you constrain them the <code>Component</code> * will be positioned at 0,0 relative to the <code>Insets</code> of the * parent <code>Container</code>. * * <p> * You can change * a component's constraints in several ways. * You can * use one of the * {@link #putConstraint putConstraint} * methods * to establish a spring * linking the edges of two components within the same container. * Or you can get the appropriate <code>SpringLayout.Constraints</code> * object using * {@link #getConstraints getConstraints} * and then modify one or more of its springs. * Or you can get the spring for a particular edge of a component * using {@link #getConstraint getConstraint}, * and modify it. * You can also associate * your own <code>SpringLayout.Constraints</code> object * with a component by specifying the constraints object * when you add the component to its container * (using * {@link Container#add(Component, Object)}). * * <p> * The <code>Spring</code> object representing each constraint * has a minimum, preferred, maximum, and current value. * The current value of the spring * is somewhere between the minimum and maximum values, * according to the formula given in the * {@link Spring#sum} method description. * When the minimum, preferred, and maximum values are the same, * the current value is always equal to them; * this inflexible spring is called a <em>strut</em>. * You can create struts using the factory method * {@link Spring#constant(int)}. * The <code>Spring</code> class also provides factory methods * for creating other kinds of springs, * including springs that depend on other springs. * * <p> * In a <code>SpringLayout</code>, the position of each edge is dependent on * the position of just one other edge. If a constraint is subsequently added * to create a new binding for an edge, the previous binding is discarded * and the edge remains dependent on a single edge. * Springs should only be attached * between edges of the container and its immediate children; the behavior * of the <code>SpringLayout</code> when presented with constraints linking * the edges of components from different containers (either internal or * external) is undefined. * * <h3> * SpringLayout vs. Other Layout Managers * </h3> * * <blockquote> * <hr> * <strong>Note:</strong> * Unlike many layout managers, * <code>SpringLayout</code> doesn't automatically set the location of * the components it manages. * If you hand-code a GUI that uses <code>SpringLayout</code>, * remember to initialize component locations by constraining the west/east * and north/south locations. * <p> * Depending on the constraints you use, * you may also need to set the size of the container explicitly. * <hr> * </blockquote> * * <p> * Despite the simplicity of <code>SpringLayout</code>, * it can emulate the behavior of most other layout managers. * For some features, * such as the line breaking provided by <code>FlowLayout</code>, * you'll need to * create a special-purpose subclass of the <code>Spring</code> class. * * <p> * <code>SpringLayout</code> also provides a way to solve * many of the difficult layout * problems that cannot be solved by nesting combinations * of <code>Box</code>es. That said, <code>SpringLayout</code> honors the * <code>LayoutManager2</code> contract correctly and so can be nested with * other layout managers -- a technique that can be preferable to * creating the constraints implied by the other layout managers. * <p> * The asymptotic complexity of the layout operation of a <code>SpringLayout</code> * is linear in the number of constraints (and/or components). * <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 Spring * @see SpringLayout.Constraints * * @author      Philip Milne * @author      Scott Violet * @author      Joe Winchester * @since       1.4 */public class SpringLayout implements LayoutManager2 {    private Map<Component, Constraints> componentConstraints = new HashMap<Component, Constraints>();     private Spring cyclicReference = Spring.constant(Spring.UNSET);    private Set<Spring> cyclicSprings;    private Set<Spring> acyclicSprings;      /**     * Specifies the top edge of a component's bounding rectangle.     */    public static final String NORTH  = "North";     /**     * Specifies the bottom edge of a component's bounding rectangle.     */    public static final String SOUTH  = "South";     /**     * Specifies the right edge of a component's bounding rectangle.     */    public static final String EAST   = "East";     /**     * Specifies the left edge of a component's bounding rectangle.     */    public static final String WEST   = "West";     /**     * Specifies the horizontal center of a component's bounding rectangle.     *     * @since 1.6     */    public static final String HORIZONTAL_CENTER   = "HorizontalCenter";     /**     * Specifies the vertical center of a component's bounding rectangle.     *     * @since 1.6     */    public static final String VERTICAL_CENTER   = "VerticalCenter";     /**     * Specifies the baseline of a component.     *     * @since 1.6     */    public static final String BASELINE   = "Baseline";     /**     * Specifies the width of a component's bounding rectangle.     *     * @since 1.6     */    public static final String WIDTH = "Width";     /**     * Specifies the height of a component's bounding rectangle.     *     * @since 1.6     */    public static final String HEIGHT = "Height";     private static String[] ALL_HORIZONTAL = {WEST, WIDTH, EAST, HORIZONTAL_CENTER};     private static String[] ALL_VERTICAL = {NORTH, HEIGHT, SOUTH, VERTICAL_CENTER, BASELINE};     /**     * A <code>Constraints</code> object holds the     * constraints that govern the way a component's size and position     * change in a container controlled by a <code>SpringLayout</code>.     * A <code>Constraints</code> object is     * like a <code>Rectangle</code>, in that it     * has <code>x</code>, <code>y</code>,     * <code>width</code>, and <code>height</code> properties.     * In the <code>Constraints</code> object, however,     * these properties have     * <code>Spring</code> values instead of integers.     * In addition,     * a <code>Constraints</code> object     * can be manipulated as four edges     * -- north, south, east, and west --     * using the <code>constraint</code> property.     *     * <p>     * The following formulas are always true     * for a <code>Constraints</code> object (here WEST and <code>x</code> are synonyms, as are and NORTH and <code>y</code>):     *     * <pre>     *               EAST = WEST + WIDTH     *              SOUTH = NORTH + HEIGHT     *  HORIZONTAL_CENTER = WEST + WIDTH/2     *    VERTICAL_CENTER = NORTH + HEIGHT/2     *  ABSOLUTE_BASELINE = NORTH + RELATIVE_BASELINE*     * </pre>     * <p>     * For example, if you have specified the WIDTH and WEST (X) location     * the EAST is calculated as WEST + WIDTH.  If you instead specified     * the WIDTH and EAST locations the WEST (X) location is then calculated     * as EAST - WIDTH.     * <p>     * [RELATIVE_BASELINE is a private constraint that is set automatically when     * the SpringLayout.Constraints(Component) constuctor is called or when     * a constraints object is registered with a SpringLayout object.]     * <p>     * <b>Note</b>: In this document,     * operators represent methods     * in the <code>Spring</code> class.     * For example, "a + b" is equal to     * <code>Spring.sum(a, b)</code>,     * and "a - b" is equal to     * <code>Spring.sum(a, Spring.minus(b))</code>.     * See the     * {@link Spring <code>Spring</code> API documentation}     * for further details     * of spring arithmetic.     *     * <p>     *     * Because a <code>Constraints</code> object's properties --     * representing its edges, size, and location -- can all be set     * independently and yet are interrelated,     * a <code>Constraints</code> object can become <em>over-constrained</em>.     * For example, if the <code>WEST</code>, <code>WIDTH</code> and     * <code>EAST</code> edges are all set, steps must be taken to ensure that     * the first of the formulas above holds.  To do this, the     * <code>Constraints</code>     * object throws away the <em>least recently set</em>     * constraint so as to make the formulas hold.     * @since 1.4     */    public static class Constraints {       private Spring x;       private Spring y;       private Spring width;       private Spring height;       private Spring east;       private Spring south;        private Spring horizontalCenter;        private Spring verticalCenter;        private Spring baseline;         private List<String> horizontalHistory = new ArrayList<String>(2);        private List<String> verticalHistory = new ArrayList<String>(2);         // Used for baseline calculations        private Component c;        /**        * Creates an empty <code>Constraints</code> object.        */       public Constraints() {       }        /**        * Creates a <code>Constraints</code> object with the        * specified values for its        * <code>x</code> and <code>y</code> properties.        * The <code>height</code> and <code>width</code> springs        * have <code>null</code> values.        *        * @param x  the spring controlling the component's <em>x</em> value        * @param y  the spring controlling the component's <em>y</em> value        */       public Constraints(Spring x, Spring y) {           setX(x);           setY(y);       }        /**        * Creates a <code>Constraints</code> object with the        * specified values for its        * <code>x</code>, <code>y</code>, <code>width</code>,        * and <code>height</code> properties.        * Note: If the <code>SpringLayout</code> class        * encounters <code>null</code> values in the        * <code>Constraints</code> object of a given component,        * it replaces them with suitable defaults.        *        * @param x  the spring value for the <code>x</code> property        * @param y  the spring value for the <code>y</code> property        * @param width  the spring value for the <code>width</code> property        * @param height  the spring value for the <code>height</code> property        */       public Constraints(Spring x, Spring y, Spring width, Spring height) {           setX(x);           setY(y);           setWidth(width);           setHeight(height);       }         /**         * Creates a <code>Constraints</code> object with         * suitable <code>x</code>, <code>y</code>, <code>width</code> and         * <code>height</code> springs for component, <code>c</code>.         * The <code>x</code> and <code>y</code> springs are constant         * springs  initialised with the component's location at         * the time this method is called. The <code>width</code> and         * <code>height</code> springs are special springs, created by         * the <code>Spring.width()</code> and <code>Spring.height()</code>         * methods, which track the size characteristics of the component         * when they change.         *         * @param c  the component whose characteristics will be reflected by this Constraints object         * @throws NullPointerException if <code>c</code> is null.         * @since 1.5         */        public Constraints(Component c) {            this.c = c;            setX(Spring.constant(c.getX()));            setY(Spring.constant(c.getY()));            setWidth(Spring.width(c));            setHeight(Spring.height(c));        }         private void pushConstraint(String name, Spring value, boolean horizontal) {            boolean valid = true;            List<String> history = horizontal ? horizontalHistory :                                                verticalHistory;            if (history.contains(name)) {                history.remove(name);                valid = false;            } else if (history.size() == 2 && value != null) {                history.remove(0);                valid = false;            }            if (value != null) {                history.add(name);            }            if (!valid) {                String[] all = horizontal ? ALL_HORIZONTAL : ALL_VERTICAL;                for (String s : all) {                    if (!history.contains(s)) {                        setConstraint(s, null);                    }                }            }        }        private Spring sum(Spring s1, Spring s2) {           return (s1 == null || s2 == null) ? null : Spring.sum(s1, s2);       }        private Spring difference(Spring s1, Spring s2) {           return (s1 == null || s2 == null) ? null : Spring.difference(s1, s2);       }         private Spring scale(Spring s, float factor) {            return (s == null) ? null : Spring.scale(s, factor);        }         private int getBaselineFromHeight(int height) {            if (height < 0) {                // Bad Scott, Bad Scott!                return -c.getBaseline(c.getPreferredSize().width,                                      -height);            }            return c.getBaseline(c.getPreferredSize().width, height);        }         private int getHeightFromBaseLine(int baseline) {            Dimension prefSize = c.getPreferredSize();            int prefHeight = prefSize.height;            int prefBaseline = c.getBaseline(prefSize.width, prefHeight);            if (prefBaseline == baseline) {                // If prefBaseline < 0, then no baseline, assume preferred                // height.                // If prefBaseline == baseline, then specified baseline                // matches preferred baseline, return preferred height                return prefHeight;            }            // Valid baseline            switch(c.getBaselineResizeBehavior()) {            case CONSTANT_DESCENT:                return prefHeight + (baseline - prefBaseline);            case CENTER_OFFSET:                return prefHeight + 2 * (baseline - prefBaseline);            case CONSTANT_ASCENT:                // Component baseline and specified baseline will NEVER                // match, fall through to default            default: // OTHER                // No way to map from baseline to height.            }            return Integer.MIN_VALUE;        }          private Spring heightToRelativeBaseline(Spring s) {            return new Spring.SpringMap(s) {                 protected int map(int i) {                    return getBaselineFromHeight(i);                 }                  protected int inv(int i) {                     return getHeightFromBaseLine(i);                 }            };        }         private Spring relativeBaselineToHeight(Spring s) {            return new Spring.SpringMap(s) {                protected int map(int i) {                    return getHeightFromBaseLine(i);                 }                  protected int inv(int i) {                    return getBaselineFromHeight(i);                 }            };        }         private boolean defined(List history, String s1, String s2) {            return history.contains(s1) && history.contains(s2);        } 

Lines 1–500 of 1233.