Home » openjdk-7 » javax » swing » [javadoc | source]

    1   /*
    2    * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package javax.swing;
   26   
   27   import java.awt.Component;
   28   import java.awt.Container;
   29   import java.awt.Dimension;
   30   import java.awt.FontMetrics;
   31   import java.awt.Insets;
   32   import java.awt.LayoutManager2;
   33   import java.awt.Rectangle;
   34   import java.util;
   35   
   36   /**
   37    * A <code>SpringLayout</code> lays out the children of its associated container
   38    * according to a set of constraints.
   39    * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a>
   40    * in <em>The Java Tutorial</em> for examples of using
   41    * <code>SpringLayout</code>.
   42    *
   43    * <p>
   44    * Each constraint,
   45    * represented by a <code>Spring</code> object,
   46    * controls the vertical or horizontal distance
   47    * between two component edges.
   48    * The edges can belong to
   49    * any child of the container,
   50    * or to the container itself.
   51    * For example,
   52    * the allowable width of a component
   53    * can be expressed using a constraint
   54    * that controls the distance between the west (left) and east (right)
   55    * edges of the component.
   56    * The allowable <em>y</em> coordinates for a component
   57    * can be expressed by constraining the distance between
   58    * the north (top) edge of the component
   59    * and the north edge of its container.
   60    *
   61    * <P>
   62    * Every child of a <code>SpringLayout</code>-controlled container,
   63    * as well as the container itself,
   64    * has exactly one set of constraints
   65    * associated with it.
   66    * These constraints are represented by
   67    * a <code>SpringLayout.Constraints</code> object.
   68    * By default,
   69    * <code>SpringLayout</code> creates constraints
   70    * that make their associated component
   71    * have the minimum, preferred, and maximum sizes
   72    * returned by the component's
   73    * {@link java.awt.Component#getMinimumSize},
   74    * {@link java.awt.Component#getPreferredSize}, and
   75    * {@link java.awt.Component#getMaximumSize}
   76    * methods. The <em>x</em> and <em>y</em> positions are initially not
   77    * constrained, so that until you constrain them the <code>Component</code>
   78    * will be positioned at 0,0 relative to the <code>Insets</code> of the
   79    * parent <code>Container</code>.
   80    *
   81    * <p>
   82    * You can change
   83    * a component's constraints in several ways.
   84    * You can
   85    * use one of the
   86    * {@link #putConstraint putConstraint}
   87    * methods
   88    * to establish a spring
   89    * linking the edges of two components within the same container.
   90    * Or you can get the appropriate <code>SpringLayout.Constraints</code>
   91    * object using
   92    * {@link #getConstraints getConstraints}
   93    * and then modify one or more of its springs.
   94    * Or you can get the spring for a particular edge of a component
   95    * using {@link #getConstraint getConstraint},
   96    * and modify it.
   97    * You can also associate
   98    * your own <code>SpringLayout.Constraints</code> object
   99    * with a component by specifying the constraints object
  100    * when you add the component to its container
  101    * (using
  102    * {@link Container#add(Component, Object)}).
  103    *
  104    * <p>
  105    * The <code>Spring</code> object representing each constraint
  106    * has a minimum, preferred, maximum, and current value.
  107    * The current value of the spring
  108    * is somewhere between the minimum and maximum values,
  109    * according to the formula given in the
  110    * {@link Spring#sum} method description.
  111    * When the minimum, preferred, and maximum values are the same,
  112    * the current value is always equal to them;
  113    * this inflexible spring is called a <em>strut</em>.
  114    * You can create struts using the factory method
  115    * {@link Spring#constant(int)}.
  116    * The <code>Spring</code> class also provides factory methods
  117    * for creating other kinds of springs,
  118    * including springs that depend on other springs.
  119    *
  120    * <p>
  121    * In a <code>SpringLayout</code>, the position of each edge is dependent on
  122    * the position of just one other edge. If a constraint is subsequently added
  123    * to create a new binding for an edge, the previous binding is discarded
  124    * and the edge remains dependent on a single edge.
  125    * Springs should only be attached
  126    * between edges of the container and its immediate children; the behavior
  127    * of the <code>SpringLayout</code> when presented with constraints linking
  128    * the edges of components from different containers (either internal or
  129    * external) is undefined.
  130    *
  131    * <h3>
  132    * SpringLayout vs. Other Layout Managers
  133    * </h3>
  134    *
  135    * <blockquote>
  136    * <hr>
  137    * <strong>Note:</strong>
  138    * Unlike many layout managers,
  139    * <code>SpringLayout</code> doesn't automatically set the location of
  140    * the components it manages.
  141    * If you hand-code a GUI that uses <code>SpringLayout</code>,
  142    * remember to initialize component locations by constraining the west/east
  143    * and north/south locations.
  144    * <p>
  145    * Depending on the constraints you use,
  146    * you may also need to set the size of the container explicitly.
  147    * <hr>
  148    * </blockquote>
  149    *
  150    * <p>
  151    * Despite the simplicity of <code>SpringLayout</code>,
  152    * it can emulate the behavior of most other layout managers.
  153    * For some features,
  154    * such as the line breaking provided by <code>FlowLayout</code>,
  155    * you'll need to
  156    * create a special-purpose subclass of the <code>Spring</code> class.
  157    *
  158    * <p>
  159    * <code>SpringLayout</code> also provides a way to solve
  160    * many of the difficult layout
  161    * problems that cannot be solved by nesting combinations
  162    * of <code>Box</code>es. That said, <code>SpringLayout</code> honors the
  163    * <code>LayoutManager2</code> contract correctly and so can be nested with
  164    * other layout managers -- a technique that can be preferable to
  165    * creating the constraints implied by the other layout managers.
  166    * <p>
  167    * The asymptotic complexity of the layout operation of a <code>SpringLayout</code>
  168    * is linear in the number of constraints (and/or components).
  169    * <p>
  170    * <strong>Warning:</strong>
  171    * Serialized objects of this class will not be compatible with
  172    * future Swing releases. The current serialization support is
  173    * appropriate for short term storage or RMI between applications running
  174    * the same version of Swing.  As of 1.4, support for long term storage
  175    * of all JavaBeans<sup><font size="-2">TM</font></sup>
  176    * has been added to the <code>java.beans</code> package.
  177    * Please see {@link java.beans.XMLEncoder}.
  178    *
  179    * @see Spring
  180    * @see SpringLayout.Constraints
  181    *
  182    * @author      Philip Milne
  183    * @author      Scott Violet
  184    * @author      Joe Winchester
  185    * @since       1.4
  186    */
  187   public class SpringLayout implements LayoutManager2 {
  188       private Map<Component, Constraints> componentConstraints = new HashMap<Component, Constraints>();
  189   
  190       private Spring cyclicReference = Spring.constant(Spring.UNSET);
  191       private Set<Spring> cyclicSprings;
  192       private Set<Spring> acyclicSprings;
  193   
  194   
  195       /**
  196        * Specifies the top edge of a component's bounding rectangle.
  197        */
  198       public static final String NORTH  = "North";
  199   
  200       /**
  201        * Specifies the bottom edge of a component's bounding rectangle.
  202        */
  203       public static final String SOUTH  = "South";
  204   
  205       /**
  206        * Specifies the right edge of a component's bounding rectangle.
  207        */
  208       public static final String EAST   = "East";
  209   
  210       /**
  211        * Specifies the left edge of a component's bounding rectangle.
  212        */
  213       public static final String WEST   = "West";
  214   
  215       /**
  216        * Specifies the horizontal center of a component's bounding rectangle.
  217        *
  218        * @since 1.6
  219        */
  220       public static final String HORIZONTAL_CENTER   = "HorizontalCenter";
  221   
  222       /**
  223        * Specifies the vertical center of a component's bounding rectangle.
  224        *
  225        * @since 1.6
  226        */
  227       public static final String VERTICAL_CENTER   = "VerticalCenter";
  228   
  229       /**
  230        * Specifies the baseline of a component.
  231        *
  232        * @since 1.6
  233        */
  234       public static final String BASELINE   = "Baseline";
  235   
  236       /**
  237        * Specifies the width of a component's bounding rectangle.
  238        *
  239        * @since 1.6
  240        */
  241       public static final String WIDTH = "Width";
  242   
  243       /**
  244        * Specifies the height of a component's bounding rectangle.
  245        *
  246        * @since 1.6
  247        */
  248       public static final String HEIGHT = "Height";
  249   
  250       private static String[] ALL_HORIZONTAL = {WEST, WIDTH, EAST, HORIZONTAL_CENTER};
  251   
  252       private static String[] ALL_VERTICAL = {NORTH, HEIGHT, SOUTH, VERTICAL_CENTER, BASELINE};
  253   
  254       /**
  255        * A <code>Constraints</code> object holds the
  256        * constraints that govern the way a component's size and position
  257        * change in a container controlled by a <code>SpringLayout</code>.
  258        * A <code>Constraints</code> object is
  259        * like a <code>Rectangle</code>, in that it
  260        * has <code>x</code>, <code>y</code>,
  261        * <code>width</code>, and <code>height</code> properties.
  262        * In the <code>Constraints</code> object, however,
  263        * these properties have
  264        * <code>Spring</code> values instead of integers.
  265        * In addition,
  266        * a <code>Constraints</code> object
  267        * can be manipulated as four edges
  268        * -- north, south, east, and west --
  269        * using the <code>constraint</code> property.
  270        *
  271        * <p>
  272        * The following formulas are always true
  273        * for a <code>Constraints</code> object (here WEST and <code>x</code> are synonyms, as are and NORTH and <code>y</code>):
  274        *
  275        * <pre>
  276        *               EAST = WEST + WIDTH
  277        *              SOUTH = NORTH + HEIGHT
  278        *  HORIZONTAL_CENTER = WEST + WIDTH/2
  279        *    VERTICAL_CENTER = NORTH + HEIGHT/2
  280        *  ABSOLUTE_BASELINE = NORTH + RELATIVE_BASELINE*
  281        * </pre>
  282        * <p>
  283        * For example, if you have specified the WIDTH and WEST (X) location
  284        * the EAST is calculated as WEST + WIDTH.  If you instead specified
  285        * the WIDTH and EAST locations the WEST (X) location is then calculated
  286        * as EAST - WIDTH.
  287        * <p>
  288        * [RELATIVE_BASELINE is a private constraint that is set automatically when
  289        * the SpringLayout.Constraints(Component) constuctor is called or when
  290        * a constraints object is registered with a SpringLayout object.]
  291        * <p>
  292        * <b>Note</b>: In this document,
  293        * operators represent methods
  294        * in the <code>Spring</code> class.
  295        * For example, "a + b" is equal to
  296        * <code>Spring.sum(a, b)</code>,
  297        * and "a - b" is equal to
  298        * <code>Spring.sum(a, Spring.minus(b))</code>.
  299        * See the
  300        * {@link Spring <code>Spring</code> API documentation}
  301        * for further details
  302        * of spring arithmetic.
  303        *
  304        * <p>
  305        *
  306        * Because a <code>Constraints</code> object's properties --
  307        * representing its edges, size, and location -- can all be set
  308        * independently and yet are interrelated,
  309        * a <code>Constraints</code> object can become <em>over-constrained</em>.
  310        * For example, if the <code>WEST</code>, <code>WIDTH</code> and
  311        * <code>EAST</code> edges are all set, steps must be taken to ensure that
  312        * the first of the formulas above holds.  To do this, the
  313        * <code>Constraints</code>
  314        * object throws away the <em>least recently set</em>
  315        * constraint so as to make the formulas hold.
  316        * @since 1.4
  317        */
  318       public static class Constraints {
  319          private Spring x;
  320          private Spring y;
  321          private Spring width;
  322          private Spring height;
  323          private Spring east;
  324          private Spring south;
  325           private Spring horizontalCenter;
  326           private Spring verticalCenter;
  327           private Spring baseline;
  328   
  329           private List<String> horizontalHistory = new ArrayList<String>(2);
  330           private List<String> verticalHistory = new ArrayList<String>(2);
  331   
  332           // Used for baseline calculations
  333           private Component c;
  334   
  335          /**
  336           * Creates an empty <code>Constraints</code> object.
  337           */
  338          public Constraints() {
  339          }
  340   
  341          /**
  342           * Creates a <code>Constraints</code> object with the
  343           * specified values for its
  344           * <code>x</code> and <code>y</code> properties.
  345           * The <code>height</code> and <code>width</code> springs
  346           * have <code>null</code> values.
  347           *
  348           * @param x  the spring controlling the component's <em>x</em> value
  349           * @param y  the spring controlling the component's <em>y</em> value
  350           */
  351          public Constraints(Spring x, Spring y) {
  352              setX(x);
  353              setY(y);
  354          }
  355   
  356          /**
  357           * Creates a <code>Constraints</code> object with the
  358           * specified values for its
  359           * <code>x</code>, <code>y</code>, <code>width</code>,
  360           * and <code>height</code> properties.
  361           * Note: If the <code>SpringLayout</code> class
  362           * encounters <code>null</code> values in the
  363           * <code>Constraints</code> object of a given component,
  364           * it replaces them with suitable defaults.
  365           *
  366           * @param x  the spring value for the <code>x</code> property
  367           * @param y  the spring value for the <code>y</code> property
  368           * @param width  the spring value for the <code>width</code> property
  369           * @param height  the spring value for the <code>height</code> property
  370           */
  371          public Constraints(Spring x, Spring y, Spring width, Spring height) {
  372              setX(x);
  373              setY(y);
  374              setWidth(width);
  375              setHeight(height);
  376          }
  377   
  378           /**
  379            * Creates a <code>Constraints</code> object with
  380            * suitable <code>x</code>, <code>y</code>, <code>width</code> and
  381            * <code>height</code> springs for component, <code>c</code>.
  382            * The <code>x</code> and <code>y</code> springs are constant
  383            * springs  initialised with the component's location at
  384            * the time this method is called. The <code>width</code> and
  385            * <code>height</code> springs are special springs, created by
  386            * the <code>Spring.width()</code> and <code>Spring.height()</code>
  387            * methods, which track the size characteristics of the component
  388            * when they change.
  389            *
  390            * @param c  the component whose characteristics will be reflected by this Constraints object
  391            * @throws NullPointerException if <code>c</code> is null.
  392            * @since 1.5
  393            */
  394           public Constraints(Component c) {
  395               this.c = c;
  396               setX(Spring.constant(c.getX()));
  397               setY(Spring.constant(c.getY()));
  398               setWidth(Spring.width(c));
  399               setHeight(Spring.height(c));
  400           }
  401   
  402           private void pushConstraint(String name, Spring value, boolean horizontal) {
  403               boolean valid = true;
  404               List<String> history = horizontal ? horizontalHistory :
  405                                                   verticalHistory;
  406               if (history.contains(name)) {
  407                   history.remove(name);
  408                   valid = false;
  409               } else if (history.size() == 2 && value != null) {
  410                   history.remove(0);
  411                   valid = false;
  412               }
  413               if (value != null) {
  414                   history.add(name);
  415               }
  416               if (!valid) {
  417                   String[] all = horizontal ? ALL_HORIZONTAL : ALL_VERTICAL;
  418                   for (String s : all) {
  419                       if (!history.contains(s)) {
  420                           setConstraint(s, null);
  421                       }
  422                   }
  423               }
  424           }
  425   
  426          private Spring sum(Spring s1, Spring s2) {
  427              return (s1 == null || s2 == null) ? null : Spring.sum(s1, s2);
  428          }
  429   
  430          private Spring difference(Spring s1, Spring s2) {
  431              return (s1 == null || s2 == null) ? null : Spring.difference(s1, s2);
  432          }
  433   
  434           private Spring scale(Spring s, float factor) {
  435               return (s == null) ? null : Spring.scale(s, factor);
  436           }
  437   
  438           private int getBaselineFromHeight(int height) {
  439               if (height < 0) {
  440                   // Bad Scott, Bad Scott!
  441                   return -c.getBaseline(c.getPreferredSize().width,
  442                                         -height);
  443               }
  444               return c.getBaseline(c.getPreferredSize().width, height);
  445           }
  446   
  447           private int getHeightFromBaseLine(int baseline) {
  448               Dimension prefSize = c.getPreferredSize();
  449               int prefHeight = prefSize.height;
  450               int prefBaseline = c.getBaseline(prefSize.width, prefHeight);
  451               if (prefBaseline == baseline) {
  452                   // If prefBaseline < 0, then no baseline, assume preferred
  453                   // height.
  454                   // If prefBaseline == baseline, then specified baseline
  455                   // matches preferred baseline, return preferred height
  456                   return prefHeight;
  457               }
  458               // Valid baseline
  459               switch(c.getBaselineResizeBehavior()) {
  460               case CONSTANT_DESCENT:
  461                   return prefHeight + (baseline - prefBaseline);
  462               case CENTER_OFFSET:
  463                   return prefHeight + 2 * (baseline - prefBaseline);
  464               case CONSTANT_ASCENT:
  465                   // Component baseline and specified baseline will NEVER
  466                   // match, fall through to default
  467               default: // OTHER
  468                   // No way to map from baseline to height.
  469               }
  470               return Integer.MIN_VALUE;
  471           }
  472   
  473            private Spring heightToRelativeBaseline(Spring s) {
  474               return new Spring.SpringMap(s) {
  475                    protected int map(int i) {
  476                       return getBaselineFromHeight(i);
  477                    }
  478   
  479                    protected int inv(int i) {
  480                        return getHeightFromBaseLine(i);
  481                    }
  482               };
  483           }
  484   
  485           private Spring relativeBaselineToHeight(Spring s) {
  486               return new Spring.SpringMap(s) {
  487                   protected int map(int i) {
  488                       return getHeightFromBaseLine(i);
  489                    }
  490   
  491                    protected int inv(int i) {
  492                       return getBaselineFromHeight(i);
  493                    }
  494               };
  495           }
  496   
  497           private boolean defined(List history, String s1, String s2) {
  498               return history.contains(s1) && history.contains(s2);
  499           }
  500   
  501          /**
  502           * Sets the <code>x</code> property,
  503           * which controls the <code>x</code> value
  504           * of a component's location.
  505           *
  506           * @param x the spring controlling the <code>x</code> value
  507           *          of a component's location
  508           *
  509           * @see #getX
  510           * @see SpringLayout.Constraints
  511           */
  512          public void setX(Spring x) {
  513              this.x = x;
  514              pushConstraint(WEST, x, true);
  515          }
  516   
  517          /**
  518           * Returns the value of the <code>x</code> property.
  519           *
  520           * @return the spring controlling the <code>x</code> value
  521           *         of a component's location
  522           *
  523           * @see #setX
  524           * @see SpringLayout.Constraints
  525           */
  526          public Spring getX() {
  527              if (x == null) {
  528                  if (defined(horizontalHistory, EAST, WIDTH)) {
  529                      x = difference(east, width);
  530                  } else if (defined(horizontalHistory, HORIZONTAL_CENTER, WIDTH)) {
  531                      x = difference(horizontalCenter, scale(width, 0.5f));
  532                  } else if (defined(horizontalHistory, HORIZONTAL_CENTER, EAST)) {
  533                      x = difference(scale(horizontalCenter, 2f), east);
  534                  }
  535              }
  536              return x;
  537          }
  538   
  539          /**
  540           * Sets the <code>y</code> property,
  541           * which controls the <code>y</code> value
  542           * of a component's location.
  543           *
  544           * @param y the spring controlling the <code>y</code> value
  545           *          of a component's location
  546           *
  547           * @see #getY
  548           * @see SpringLayout.Constraints
  549           */
  550          public void setY(Spring y) {
  551              this.y = y;
  552              pushConstraint(NORTH, y, false);
  553          }
  554   
  555          /**
  556           * Returns the value of the <code>y</code> property.
  557           *
  558           * @return the spring controlling the <code>y</code> value
  559           *         of a component's location
  560           *
  561           * @see #setY
  562           * @see SpringLayout.Constraints
  563           */
  564          public Spring getY() {
  565              if (y == null) {
  566                  if (defined(verticalHistory, SOUTH, HEIGHT)) {
  567                      y = difference(south, height);
  568                  } else if (defined(verticalHistory, VERTICAL_CENTER, HEIGHT)) {
  569                      y = difference(verticalCenter, scale(height, 0.5f));
  570                  } else if (defined(verticalHistory, VERTICAL_CENTER, SOUTH)) {
  571                      y = difference(scale(verticalCenter, 2f), south);
  572                  } else if (defined(verticalHistory, BASELINE, HEIGHT)) {
  573                      y = difference(baseline, heightToRelativeBaseline(height));
  574                  } else if (defined(verticalHistory, BASELINE, SOUTH)) {
  575                      y = scale(difference(baseline, heightToRelativeBaseline(south)), 2f);
  576   /*
  577                  } else if (defined(verticalHistory, BASELINE, VERTICAL_CENTER)) {
  578                      y = scale(difference(baseline, heightToRelativeBaseline(scale(verticalCenter, 2))), 1f/(1-2*0.5f));
  579   */
  580                  }
  581              }
  582              return y;
  583          }
  584   
  585          /**
  586           * Sets the <code>width</code> property,
  587           * which controls the width of a component.
  588           *
  589           * @param width the spring controlling the width of this
  590           * <code>Constraints</code> object
  591           *
  592           * @see #getWidth
  593           * @see SpringLayout.Constraints
  594           */
  595          public void setWidth(Spring width) {
  596              this.width = width;
  597              pushConstraint(WIDTH, width, true);
  598          }
  599   
  600          /**
  601           * Returns the value of the <code>width</code> property.
  602           *
  603           * @return the spring controlling the width of a component
  604           *
  605           * @see #setWidth
  606           * @see SpringLayout.Constraints
  607           */
  608          public Spring getWidth() {
  609              if (width == null) {
  610                  if (horizontalHistory.contains(EAST)) {
  611                      width = difference(east, getX());
  612                  } else if (horizontalHistory.contains(HORIZONTAL_CENTER)) {
  613                      width = scale(difference(horizontalCenter, getX()), 2f);
  614                  }
  615              }
  616              return width;
  617          }
  618   
  619          /**
  620           * Sets the <code>height</code> property,
  621           * which controls the height of a component.
  622           *
  623           * @param height the spring controlling the height of this <code>Constraints</code>
  624           * object
  625           *
  626           * @see #getHeight
  627           * @see SpringLayout.Constraints
  628           */
  629          public void setHeight(Spring height) {
  630              this.height = height;
  631              pushConstraint(HEIGHT, height, false);
  632          }
  633   
  634          /**
  635           * Returns the value of the <code>height</code> property.
  636           *
  637           * @return the spring controlling the height of a component
  638           *
  639           * @see #setHeight
  640           * @see SpringLayout.Constraints
  641           */
  642          public Spring getHeight() {
  643              if (height == null) {
  644                  if (verticalHistory.contains(SOUTH)) {
  645                      height = difference(south, getY());
  646                  } else if (verticalHistory.contains(VERTICAL_CENTER)) {
  647                      height = scale(difference(verticalCenter, getY()), 2f);
  648                  } else if (verticalHistory.contains(BASELINE)) {
  649                      height = relativeBaselineToHeight(difference(baseline, getY()));
  650                  }
  651              }
  652              return height;
  653          }
  654   
  655          private void setEast(Spring east) {
  656              this.east = east;
  657              pushConstraint(EAST, east, true);
  658          }
  659   
  660          private Spring getEast() {
  661              if (east == null) {
  662                  east = sum(getX(), getWidth());
  663              }
  664              return east;
  665          }
  666   
  667          private void setSouth(Spring south) {
  668              this.south = south;
  669              pushConstraint(SOUTH, south, false);
  670          }
  671   
  672          private Spring getSouth() {
  673              if (south == null) {
  674                  south = sum(getY(), getHeight());
  675              }
  676              return south;
  677          }
  678   
  679           private Spring getHorizontalCenter() {
  680               if (horizontalCenter == null) {
  681                   horizontalCenter = sum(getX(), scale(getWidth(), 0.5f));
  682               }
  683               return horizontalCenter;
  684           }
  685   
  686           private void setHorizontalCenter(Spring horizontalCenter) {
  687               this.horizontalCenter = horizontalCenter;
  688               pushConstraint(HORIZONTAL_CENTER, horizontalCenter, true);
  689           }
  690   
  691           private Spring getVerticalCenter() {
  692               if (verticalCenter == null) {
  693                   verticalCenter = sum(getY(), scale(getHeight(), 0.5f));
  694               }
  695               return verticalCenter;
  696           }
  697   
  698           private void setVerticalCenter(Spring verticalCenter) {
  699               this.verticalCenter = verticalCenter;
  700               pushConstraint(VERTICAL_CENTER, verticalCenter, false);
  701           }
  702   
  703           private Spring getBaseline() {
  704               if (baseline == null) {
  705                   baseline = sum(getY(), heightToRelativeBaseline(getHeight()));
  706               }
  707               return baseline;
  708           }
  709   
  710           private void setBaseline(Spring baseline) {
  711               this.baseline = baseline;
  712               pushConstraint(BASELINE, baseline, false);
  713           }
  714   
  715          /**
  716           * Sets the spring controlling the specified edge.
  717           * The edge must have one of the following values:
  718           * <code>SpringLayout.NORTH</code>,
  719           * <code>SpringLayout.SOUTH</code>,
  720           * <code>SpringLayout.EAST</code>,
  721           * <code>SpringLayout.WEST</code>,
  722           * <code>SpringLayout.HORIZONTAL_CENTER</code>,
  723           * <code>SpringLayout.VERTICAL_CENTER</code>,
  724           * <code>SpringLayout.BASELINE</code>,
  725           * <code>SpringLayout.WIDTH</code> or
  726           * <code>SpringLayout.HEIGHT</code>.
  727           * For any other <code>String</code> value passed as the edge,
  728           * no action is taken. For a <code>null</code> edge, a
  729           * <code>NullPointerException</code> is thrown.
  730           * <p/>
  731           * <b>Note:</b> This method can affect {@code x} and {@code y} values
  732           * previously set for this {@code Constraints}.
  733           *
  734           * @param edgeName the edge to be set
  735           * @param s the spring controlling the specified edge
  736           *
  737           * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
  738           *
  739           * @see #getConstraint
  740           * @see #NORTH
  741           * @see #SOUTH
  742           * @see #EAST
  743           * @see #WEST
  744           * @see #HORIZONTAL_CENTER
  745           * @see #VERTICAL_CENTER
  746           * @see #BASELINE
  747           * @see #WIDTH
  748           * @see #HEIGHT
  749           * @see SpringLayout.Constraints
  750           */
  751          public void setConstraint(String edgeName, Spring s) {
  752              edgeName = edgeName.intern();
  753              if (edgeName == WEST) {
  754                  setX(s);
  755              } else if (edgeName == NORTH) {
  756                  setY(s);
  757              } else if (edgeName == EAST) {
  758                  setEast(s);
  759              } else if (edgeName == SOUTH) {
  760                  setSouth(s);
  761              } else if (edgeName == HORIZONTAL_CENTER) {
  762                  setHorizontalCenter(s);
  763              } else if (edgeName == WIDTH) {
  764                  setWidth(s);
  765              } else if (edgeName == HEIGHT) {
  766                  setHeight(s);
  767              } else if (edgeName == VERTICAL_CENTER) {
  768                  setVerticalCenter(s);
  769              } else if (edgeName == BASELINE) {
  770                  setBaseline(s);
  771              }
  772          }
  773   
  774          /**
  775           * Returns the value of the specified edge, which may be
  776           * a derived value, or even <code>null</code>.
  777           * The edge must have one of the following values:
  778           * <code>SpringLayout.NORTH</code>,
  779           * <code>SpringLayout.SOUTH</code>,
  780           * <code>SpringLayout.EAST</code>,
  781           * <code>SpringLayout.WEST</code>,
  782           * <code>SpringLayout.HORIZONTAL_CENTER</code>,
  783           * <code>SpringLayout.VERTICAL_CENTER</code>,
  784           * <code>SpringLayout.BASELINE</code>,
  785           * <code>SpringLayout.WIDTH</code> or
  786           * <code>SpringLayout.HEIGHT</code>.
  787           * For any other <code>String</code> value passed as the edge,
  788           * <code>null</code> will be returned. Throws
  789           * <code>NullPointerException</code> for a <code>null</code> edge.
  790           *
  791           * @param edgeName the edge whose value
  792           *                 is to be returned
  793           *
  794           * @return the spring controlling the specified edge, may be <code>null</code>
  795           *
  796           * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
  797           *
  798           * @see #setConstraint
  799           * @see #NORTH
  800           * @see #SOUTH
  801           * @see #EAST
  802           * @see #WEST
  803           * @see #HORIZONTAL_CENTER
  804           * @see #VERTICAL_CENTER
  805           * @see #BASELINE
  806           * @see #WIDTH
  807           * @see #HEIGHT
  808           * @see SpringLayout.Constraints
  809           */
  810          public Spring getConstraint(String edgeName) {
  811              edgeName = edgeName.intern();
  812              return (edgeName == WEST)  ? getX() :
  813                      (edgeName == NORTH) ? getY() :
  814                      (edgeName == EAST)  ? getEast() :
  815                      (edgeName == SOUTH) ? getSouth() :
  816                      (edgeName == WIDTH)  ? getWidth() :
  817                      (edgeName == HEIGHT) ? getHeight() :
  818                      (edgeName == HORIZONTAL_CENTER) ? getHorizontalCenter() :
  819                      (edgeName == VERTICAL_CENTER)  ? getVerticalCenter() :
  820                      (edgeName == BASELINE) ? getBaseline() :
  821                     null;
  822          }
  823   
  824          /*pp*/ void reset() {
  825              Spring[] allSprings = {x, y, width, height, east, south,
  826                  horizontalCenter, verticalCenter, baseline};
  827              for (Spring s : allSprings) {
  828                  if (s != null) {
  829                      s.setValue(Spring.UNSET);
  830                  }
  831              }
  832          }
  833      }
  834   
  835      private static class SpringProxy extends Spring {
  836          private String edgeName;
  837          private Component c;
  838          private SpringLayout l;
  839   
  840          public SpringProxy(String edgeName, Component c, SpringLayout l) {
  841              this.edgeName = edgeName;
  842              this.c = c;
  843              this.l = l;
  844          }
  845   
  846          private Spring getConstraint() {
  847              return l.getConstraints(c).getConstraint(edgeName);
  848          }
  849   
  850          public int getMinimumValue() {
  851              return getConstraint().getMinimumValue();
  852          }
  853   
  854          public int getPreferredValue() {
  855              return getConstraint().getPreferredValue();
  856          }
  857   
  858          public int getMaximumValue() {
  859              return getConstraint().getMaximumValue();
  860          }
  861   
  862          public int getValue() {
  863              return getConstraint().getValue();
  864          }
  865   
  866          public void setValue(int size) {
  867              getConstraint().setValue(size);
  868          }
  869   
  870          /*pp*/ boolean isCyclic(SpringLayout l) {
  871              return l.isCyclic(getConstraint());
  872          }
  873   
  874          public String toString() {
  875              return "SpringProxy for " + edgeName + " edge of " + c.getName() + ".";
  876          }
  877       }
  878   
  879       /**
  880        * Constructs a new <code>SpringLayout</code>.
  881        */
  882       public SpringLayout() {}
  883   
  884       private void resetCyclicStatuses() {
  885           cyclicSprings = new HashSet<Spring>();
  886           acyclicSprings = new HashSet<Spring>();
  887       }
  888   
  889       private void setParent(Container p) {
  890           resetCyclicStatuses();
  891           Constraints pc = getConstraints(p);
  892   
  893           pc.setX(Spring.constant(0));
  894           pc.setY(Spring.constant(0));
  895           // The applyDefaults() method automatically adds width and
  896           // height springs that delegate their calculations to the
  897           // getMinimumSize(), getPreferredSize() and getMaximumSize()
  898           // methods of the relevant component. In the case of the
  899           // parent this will cause an infinite loop since these
  900           // methods, in turn, delegate their calculations to the
  901           // layout manager. Check for this case and replace the
  902           // the springs that would cause this problem with a
  903           // constant springs that supply default values.
  904           Spring width = pc.getWidth();
  905           if (width instanceof Spring.WidthSpring && ((Spring.WidthSpring)width).c == p) {
  906               pc.setWidth(Spring.constant(0, 0, Integer.MAX_VALUE));
  907           }
  908           Spring height = pc.getHeight();
  909           if (height instanceof Spring.HeightSpring && ((Spring.HeightSpring)height).c == p) {
  910               pc.setHeight(Spring.constant(0, 0, Integer.MAX_VALUE));
  911           }
  912       }
  913   
  914       /*pp*/ boolean isCyclic(Spring s) {
  915           if (s == null) {
  916               return false;
  917           }
  918           if (cyclicSprings.contains(s)) {
  919               return true;
  920           }
  921           if (acyclicSprings.contains(s)) {
  922               return false;
  923           }
  924           cyclicSprings.add(s);
  925           boolean result = s.isCyclic(this);
  926           if (!result) {
  927               acyclicSprings.add(s);
  928               cyclicSprings.remove(s);
  929           }
  930           else {
  931               System.err.println(s + " is cyclic. ");
  932           }
  933           return result;
  934       }
  935   
  936       private Spring abandonCycles(Spring s) {
  937           return isCyclic(s) ? cyclicReference : s;
  938       }
  939   
  940       // LayoutManager methods.
  941   
  942       /**
  943        * Has no effect,
  944        * since this layout manager does not
  945        * use a per-component string.
  946        */
  947       public void addLayoutComponent(String name, Component c) {}
  948   
  949       /**
  950        * Removes the constraints associated with the specified component.
  951        *
  952        * @param c the component being removed from the container
  953        */
  954       public void removeLayoutComponent(Component c) {
  955           componentConstraints.remove(c);
  956       }
  957   
  958       private static Dimension addInsets(int width, int height, Container p) {
  959           Insets i = p.getInsets();
  960           return new Dimension(width + i.left + i.right, height + i.top + i.bottom);
  961       }
  962   
  963       public Dimension minimumLayoutSize(Container parent) {
  964           setParent(parent);
  965           Constraints pc = getConstraints(parent);
  966           return addInsets(abandonCycles(pc.getWidth()).getMinimumValue(),
  967                            abandonCycles(pc.getHeight()).getMinimumValue(),
  968                            parent);
  969       }
  970   
  971       public Dimension preferredLayoutSize(Container parent) {
  972           setParent(parent);
  973           Constraints pc = getConstraints(parent);
  974           return addInsets(abandonCycles(pc.getWidth()).getPreferredValue(),
  975                            abandonCycles(pc.getHeight()).getPreferredValue(),
  976                            parent);
  977       }
  978   
  979       // LayoutManager2 methods.
  980   
  981       public Dimension maximumLayoutSize(Container parent) {
  982           setParent(parent);
  983           Constraints pc = getConstraints(parent);
  984           return addInsets(abandonCycles(pc.getWidth()).getMaximumValue(),
  985                            abandonCycles(pc.getHeight()).getMaximumValue(),
  986                            parent);
  987       }
  988   
  989       /**
  990        * If <code>constraints</code> is an instance of
  991        * <code>SpringLayout.Constraints</code>,
  992        * associates the constraints with the specified component.
  993        * <p>
  994        * @param   component the component being added
  995        * @param   constraints the component's constraints
  996        *
  997        * @see SpringLayout.Constraints
  998        */
  999       public void addLayoutComponent(Component component, Object constraints) {
 1000           if (constraints instanceof Constraints) {
 1001               putConstraints(component, (Constraints)constraints);
 1002           }
 1003       }
 1004   
 1005       /**
 1006        * Returns 0.5f (centered).
 1007        */
 1008       public float getLayoutAlignmentX(Container p) {
 1009           return 0.5f;
 1010       }
 1011   
 1012       /**
 1013        * Returns 0.5f (centered).
 1014        */
 1015       public float getLayoutAlignmentY(Container p) {
 1016           return 0.5f;
 1017       }
 1018   
 1019       public void invalidateLayout(Container p) {}
 1020   
 1021       // End of LayoutManger2 methods
 1022   
 1023      /**
 1024        * Links edge <code>e1</code> of component <code>c1</code> to
 1025        * edge <code>e2</code> of component <code>c2</code>,
 1026        * with a fixed distance between the edges. This
 1027        * constraint will cause the assignment
 1028        * <pre>
 1029        *     value(e1, c1) = value(e2, c2) + pad</pre>
 1030        * to take place during all subsequent layout operations.
 1031        * <p>
 1032        * @param   e1 the edge of the dependent
 1033        * @param   c1 the component of the dependent
 1034        * @param   pad the fixed distance between dependent and anchor
 1035        * @param   e2 the edge of the anchor
 1036        * @param   c2 the component of the anchor
 1037        *
 1038        * @see #putConstraint(String, Component, Spring, String, Component)
 1039        */
 1040       public void putConstraint(String e1, Component c1, int pad, String e2, Component c2) {
 1041           putConstraint(e1, c1, Spring.constant(pad), e2, c2);
 1042       }
 1043   
 1044       /**
 1045        * Links edge <code>e1</code> of component <code>c1</code> to
 1046        * edge <code>e2</code> of component <code>c2</code>. As edge
 1047        * <code>(e2, c2)</code> changes value, edge <code>(e1, c1)</code> will
 1048        * be calculated by taking the (spring) sum of <code>(e2, c2)</code>
 1049        * and <code>s</code>.
 1050        * Each edge must have one of the following values:
 1051        * <code>SpringLayout.NORTH</code>,
 1052        * <code>SpringLayout.SOUTH</code>,
 1053        * <code>SpringLayout.EAST</code>,
 1054        * <code>SpringLayout.WEST</code>,
 1055        * <code>SpringLayout.VERTICAL_CENTER</code>,
 1056        * <code>SpringLayout.HORIZONTAL_CENTER</code> or
 1057        * <code>SpringLayout.BASELINE</code>.
 1058        * <p>
 1059        * @param   e1 the edge of the dependent
 1060        * @param   c1 the component of the dependent
 1061        * @param   s the spring linking dependent and anchor
 1062        * @param   e2 the edge of the anchor
 1063        * @param   c2 the component of the anchor
 1064        *
 1065        * @see #putConstraint(String, Component, int, String, Component)
 1066        * @see #NORTH
 1067        * @see #SOUTH
 1068        * @see #EAST
 1069        * @see #WEST
 1070        * @see #VERTICAL_CENTER
 1071        * @see #HORIZONTAL_CENTER
 1072        * @see #BASELINE
 1073        */
 1074       public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2) {
 1075           putConstraint(e1, c1, Spring.sum(s, getConstraint(e2, c2)));
 1076       }
 1077   
 1078       private void putConstraint(String e, Component c, Spring s) {
 1079           if (s != null) {
 1080               getConstraints(c).setConstraint(e, s);
 1081           }
 1082        }
 1083   
 1084       private Constraints applyDefaults(Component c, Constraints constraints) {
 1085           if (constraints == null) {
 1086               constraints = new Constraints();
 1087           }
 1088           if (constraints.c == null) {
 1089               constraints.c = c;
 1090           }
 1091           if (constraints.horizontalHistory.size() < 2) {
 1092               applyDefaults(constraints, WEST, Spring.constant(0), WIDTH,
 1093                             Spring.width(c), constraints.horizontalHistory);
 1094           }
 1095           if (constraints.verticalHistory.size() < 2) {
 1096               applyDefaults(constraints, NORTH, Spring.constant(0), HEIGHT,
 1097                             Spring.height(c), constraints.verticalHistory);
 1098           }
 1099           return constraints;
 1100       }
 1101   
 1102       private void applyDefaults(Constraints constraints, String name1,
 1103                                  Spring spring1, String name2, Spring spring2,
 1104                                  List<String> history) {
 1105           if (history.size() == 0) {
 1106               constraints.setConstraint(name1, spring1);
 1107               constraints.setConstraint(name2, spring2);
 1108           } else {
 1109               // At this point there must be exactly one constraint defined already.
 1110               // Check width/height first.
 1111               if (constraints.getConstraint(name2) == null) {
 1112                   constraints.setConstraint(name2, spring2);
 1113               } else {
 1114                   // If width/height is already defined, install a default for x/y.
 1115                   constraints.setConstraint(name1, spring1);
 1116               }
 1117               // Either way, leave the user's constraint topmost on the stack.
 1118               Collections.rotate(history, 1);
 1119           }
 1120       }
 1121   
 1122       private void putConstraints(Component component, Constraints constraints) {
 1123           componentConstraints.put(component, applyDefaults(component, constraints));
 1124       }
 1125   
 1126       /**
 1127        * Returns the constraints for the specified component.
 1128        * Note that,
 1129        * unlike the <code>GridBagLayout</code>
 1130        * <code>getConstraints</code> method,
 1131        * this method does not clone constraints.
 1132        * If no constraints
 1133        * have been associated with this component,
 1134        * this method
 1135        * returns a default constraints object positioned at
 1136        * 0,0 relative to the parent's Insets and its width/height
 1137        * constrained to the minimum, maximum, and preferred sizes of the
 1138        * component. The size characteristics
 1139        * are not frozen at the time this method is called;
 1140        * instead this method returns a constraints object
 1141        * whose characteristics track the characteristics
 1142        * of the component as they change.
 1143        *
 1144        * @param       c the component whose constraints will be returned
 1145        *
 1146        * @return      the constraints for the specified component
 1147        */
 1148       public Constraints getConstraints(Component c) {
 1149          Constraints result = componentConstraints.get(c);
 1150          if (result == null) {
 1151              if (c instanceof javax.swing.JComponent) {
 1152                   Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class);
 1153                   if (cp instanceof Constraints) {
 1154                       return applyDefaults(c, (Constraints)cp);
 1155                   }
 1156               }
 1157               result = new Constraints();
 1158               putConstraints(c, result);
 1159          }
 1160          return result;
 1161       }
 1162   
 1163       /**
 1164        * Returns the spring controlling the distance between
 1165        * the specified edge of
 1166        * the component and the top or left edge of its parent. This
 1167        * method, instead of returning the current binding for the
 1168        * edge, returns a proxy that tracks the characteristics
 1169        * of the edge even if the edge is subsequently rebound.
 1170        * Proxies are intended to be used in builder envonments
 1171        * where it is useful to allow the user to define the
 1172        * constraints for a layout in any order. Proxies do, however,
 1173        * provide the means to create cyclic dependencies amongst
 1174        * the constraints of a layout. Such cycles are detected
 1175        * internally by <code>SpringLayout</code> so that
 1176        * the layout operation always terminates.
 1177        *
 1178        * @param edgeName must be one of
 1179        * <code>SpringLayout.NORTH</code>,
 1180        * <code>SpringLayout.SOUTH</code>,
 1181        * <code>SpringLayout.EAST</code>,
 1182        * <code>SpringLayout.WEST</code>,
 1183        * <code>SpringLayout.VERTICAL_CENTER</code>,
 1184        * <code>SpringLayout.HORIZONTAL_CENTER</code> or
 1185        * <code>SpringLayout.BASELINE</code>
 1186        * @param c the component whose edge spring is desired
 1187        *
 1188        * @return a proxy for the spring controlling the distance between the
 1189        *         specified edge and the top or left edge of its parent
 1190        *
 1191        * @see #NORTH
 1192        * @see #SOUTH
 1193        * @see #EAST
 1194        * @see #WEST
 1195        * @see #VERTICAL_CENTER
 1196        * @see #HORIZONTAL_CENTER
 1197        * @see #BASELINE
 1198        */
 1199       public Spring getConstraint(String edgeName, Component c) {
 1200           // The interning here is unnecessary; it was added for efficiency.
 1201           edgeName = edgeName.intern();
 1202           return new SpringProxy(edgeName, c, this);
 1203       }
 1204   
 1205       public void layoutContainer(Container parent) {
 1206           setParent(parent);
 1207   
 1208           int n = parent.getComponentCount();
 1209           getConstraints(parent).reset();
 1210           for (int i = 0 ; i < n ; i++) {
 1211               getConstraints(parent.getComponent(i)).reset();
 1212           }
 1213   
 1214           Insets insets = parent.getInsets();
 1215           Constraints pc = getConstraints(parent);
 1216           abandonCycles(pc.getX()).setValue(0);
 1217           abandonCycles(pc.getY()).setValue(0);
 1218           abandonCycles(pc.getWidth()).setValue(parent.getWidth() -
 1219                                                 insets.left - insets.right);
 1220           abandonCycles(pc.getHeight()).setValue(parent.getHeight() -
 1221                                                  insets.top - insets.bottom);
 1222   
 1223           for (int i = 0 ; i < n ; i++) {
 1224               Component c = parent.getComponent(i);
 1225               Constraints cc = getConstraints(c);
 1226               int x = abandonCycles(cc.getX()).getValue();
 1227               int y = abandonCycles(cc.getY()).getValue();
 1228               int width = abandonCycles(cc.getWidth()).getValue();
 1229               int height = abandonCycles(cc.getHeight()).getValue();
 1230               c.setBounds(insets.left + x, insets.top + y, width, height);
 1231           }
 1232       }
 1233   }

Home » openjdk-7 » javax » swing » [javadoc | source]