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

    1   /*
    2    * Copyright (c) 2006, 2010, 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.Insets;
   31   import java.awt.LayoutManager2;
   32   import java.util;
   33   import static java.awt.Component.BaselineResizeBehavior;
   34   import static javax.swing.LayoutStyle.ComponentPlacement;
   35   import static javax.swing.SwingConstants.HORIZONTAL;
   36   import static javax.swing.SwingConstants.VERTICAL;
   37   
   38   /**
   39    * {@code GroupLayout} is a {@code LayoutManager} that hierarchically
   40    * groups components in order to position them in a {@code Container}.
   41    * {@code GroupLayout} is intended for use by builders, but may be
   42    * hand-coded as well.
   43    * Grouping is done by instances of the {@link Group Group} class. {@code
   44    * GroupLayout} supports two types of groups. A sequential group
   45    * positions its child elements sequentially, one after another. A
   46    * parallel group aligns its child elements in one of four ways.
   47    * <p>
   48    * Each group may contain any number of elements, where an element is
   49    * a {@code Group}, {@code Component}, or gap. A gap can be thought
   50    * of as an invisible component with a minimum, preferred and maximum
   51    * size. In addition {@code GroupLayout} supports a preferred gap,
   52    * whose value comes from {@code LayoutStyle}.
   53    * <p>
   54    * Elements are similar to a spring. Each element has a range as
   55    * specified by a minimum, preferred and maximum.  Gaps have either a
   56    * developer-specified range, or a range determined by {@code
   57    * LayoutStyle}. The range for {@code Component}s is determined from
   58    * the {@code Component}'s {@code getMinimumSize}, {@code
   59    * getPreferredSize} and {@code getMaximumSize} methods. In addition,
   60    * when adding {@code Component}s you may specify a particular range
   61    * to use instead of that from the component. The range for a {@code
   62    * Group} is determined by the type of group. A {@code ParallelGroup}'s
   63    * range is the maximum of the ranges of its elements. A {@code
   64    * SequentialGroup}'s range is the sum of the ranges of its elements.
   65    * <p>
   66    * {@code GroupLayout} treats each axis independently.  That is, there
   67    * is a group representing the horizontal axis, and a group
   68    * representing the vertical axis.  The horizontal group is
   69    * responsible for determining the minimum, preferred and maximum size
   70    * along the horizontal axis as well as setting the x and width of the
   71    * components contained in it. The vertical group is responsible for
   72    * determining the minimum, preferred and maximum size along the
   73    * vertical axis as well as setting the y and height of the
   74    * components contained in it. Each {@code Component} must exist in both
   75    * a horizontal and vertical group, otherwise an {@code IllegalStateException}
   76    * is thrown during layout, or when the minimum, preferred or
   77    * maximum size is requested.
   78    * <p>
   79    * The following diagram shows a sequential group along the horizontal
   80    * axis. The sequential group contains three components. A parallel group
   81    * was used along the vertical axis.
   82    * <p align="center">
   83    * <img src="doc-files/groupLayout.1.gif">
   84    * <p>
   85    * To reinforce that each axis is treated independently the diagram shows
   86    * the range of each group and element along each axis. The
   87    * range of each component has been projected onto the axes,
   88    * and the groups are rendered in blue (horizontal) and red (vertical).
   89    * For readability there is a gap between each of the elements in the
   90    * sequential group.
   91    * <p>
   92    * The sequential group along the horizontal axis is rendered as a solid
   93    * blue line. Notice the sequential group is the sum of the children elements
   94    * it contains.
   95    * <p>
   96    * Along the vertical axis the parallel group is the maximum of the height
   97    * of each of the components. As all three components have the same height,
   98    * the parallel group has the same height.
   99    * <p>
  100    * The following diagram shows the same three components, but with the
  101    * parallel group along the horizontal axis and the sequential group along
  102    * the vertical axis.
  103    * <p>
  104    * <p align="center">
  105    * <img src="doc-files/groupLayout.2.gif">
  106    * <p>
  107    * As {@code c1} is the largest of the three components, the parallel
  108    * group is sized to {@code c1}. As {@code c2} and {@code c3} are smaller
  109    * than {@code c1} they are aligned based on the alignment specified
  110    * for the component (if specified) or the default alignment of the
  111    * parallel group. In the diagram {@code c2} and {@code c3} were created
  112    * with an alignment of {@code LEADING}. If the component orientation were
  113    * right-to-left then {@code c2} and {@code c3} would be positioned on
  114    * the opposite side.
  115    * <p>
  116    * The following diagram shows a sequential group along both the horizontal
  117    * and vertical axis.
  118    * <p align="center">
  119    * <img src="doc-files/groupLayout.3.gif">
  120    * <p>
  121    * {@code GroupLayout} provides the ability to insert gaps between
  122    * {@code Component}s. The size of the gap is determined by an
  123    * instance of {@code LayoutStyle}. This may be turned on using the
  124    * {@code setAutoCreateGaps} method.  Similarly, you may use
  125    * the {@code setAutoCreateContainerGaps} method to insert gaps
  126    * between components that touch the edge of the parent container and the
  127    * container.
  128    * <p>
  129    * The following builds a panel consisting of two labels in
  130    * one column, followed by two textfields in the next column:
  131    * <pre>
  132    *   JComponent panel = ...;
  133    *   GroupLayout layout = new GroupLayout(panel);
  134    *   panel.setLayout(layout);
  135    *
  136    *   // Turn on automatically adding gaps between components
  137    *   layout.setAutoCreateGaps(true);
  138    *
  139    *   // Turn on automatically creating gaps between components that touch
  140    *   // the edge of the container and the container.
  141    *   layout.setAutoCreateContainerGaps(true);
  142    *
  143    *   // Create a sequential group for the horizontal axis.
  144    *
  145    *   GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
  146    *
  147    *   // The sequential group in turn contains two parallel groups.
  148    *   // One parallel group contains the labels, the other the text fields.
  149    *   // Putting the labels in a parallel group along the horizontal axis
  150    *   // positions them at the same x location.
  151    *   //
  152    *   // Variable indentation is used to reinforce the level of grouping.
  153    *   hGroup.addGroup(layout.createParallelGroup().
  154    *            addComponent(label1).addComponent(label2));
  155    *   hGroup.addGroup(layout.createParallelGroup().
  156    *            addComponent(tf1).addComponent(tf2));
  157    *   layout.setHorizontalGroup(hGroup);
  158    *
  159    *   // Create a sequential group for the vertical axis.
  160    *   GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
  161    *
  162    *   // The sequential group contains two parallel groups that align
  163    *   // the contents along the baseline. The first parallel group contains
  164    *   // the first label and text field, and the second parallel group contains
  165    *   // the second label and text field. By using a sequential group
  166    *   // the labels and text fields are positioned vertically after one another.
  167    *   vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
  168    *            addComponent(label1).addComponent(tf1));
  169    *   vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
  170    *            addComponent(label2).addComponent(tf2));
  171    *   layout.setVerticalGroup(vGroup);
  172    * </pre>
  173    * <p>
  174    * When run the following is produced.
  175    * <p align="center">
  176    * <img src="doc-files/groupLayout.example.png">
  177    * <p>
  178    * This layout consists of the following.
  179    * <ul><li>The horizontal axis consists of a sequential group containing two
  180    *         parallel groups.  The first parallel group contains the labels,
  181    *         and the second parallel group contains the text fields.
  182    *     <li>The vertical axis consists of a sequential group
  183    *         containing two parallel groups.  The parallel groups are configured
  184    *         to align their components along the baseline. The first parallel
  185    *         group contains the first label and first text field, and
  186    *         the second group consists of the second label and second
  187    *         text field.
  188    * </ul>
  189    * There are a couple of things to notice in this code:
  190    * <ul>
  191    *   <li>You need not explicitly add the components to the container; this
  192    *       is indirectly done by using one of the {@code add} methods of
  193    *       {@code Group}.
  194    *   <li>The various {@code add} methods return
  195    *       the caller.  This allows for easy chaining of invocations.  For
  196    *       example, {@code group.addComponent(label1).addComponent(label2);} is
  197    *       equivalent to
  198    *       {@code group.addComponent(label1); group.addComponent(label2);}.
  199    *   <li>There are no public constructors for {@code Group}s; instead
  200    *       use the create methods of {@code GroupLayout}.
  201    * </ul>
  202    *
  203    * @author Tomas Pavek
  204    * @author Jan Stola
  205    * @author Scott Violet
  206    * @since 1.6
  207    */
  208   public class GroupLayout implements LayoutManager2 {
  209       // Used in size calculations
  210       private static final int MIN_SIZE = 0;
  211   
  212       private static final int PREF_SIZE = 1;
  213   
  214       private static final int MAX_SIZE = 2;
  215   
  216       // Used by prepare, indicates min, pref or max isn't going to be used.
  217       private static final int SPECIFIC_SIZE = 3;
  218   
  219       private static final int UNSET = Integer.MIN_VALUE;
  220   
  221       /**
  222        * Indicates the size from the component or gap should be used for a
  223        * particular range value.
  224        *
  225        * @see Group
  226        */
  227       public static final int DEFAULT_SIZE = -1;
  228   
  229       /**
  230        * Indicates the preferred size from the component or gap should
  231        * be used for a particular range value.
  232        *
  233        * @see Group
  234        */
  235       public static final int PREFERRED_SIZE = -2;
  236   
  237       // Whether or not we automatically try and create the preferred
  238       // padding between components.
  239       private boolean autocreatePadding;
  240   
  241       // Whether or not we automatically try and create the preferred
  242       // padding between components the touch the edge of the container and
  243       // the container.
  244       private boolean autocreateContainerPadding;
  245   
  246       /**
  247        * Group responsible for layout along the horizontal axis.  This is NOT
  248        * the user specified group, use getHorizontalGroup to dig that out.
  249        */
  250       private Group horizontalGroup;
  251   
  252       /**
  253        * Group responsible for layout along the vertical axis.  This is NOT
  254        * the user specified group, use getVerticalGroup to dig that out.
  255        */
  256       private Group verticalGroup;
  257   
  258       // Maps from Component to ComponentInfo.  This is used for tracking
  259       // information specific to a Component.
  260       private Map<Component,ComponentInfo> componentInfos;
  261   
  262       // Container we're doing layout for.
  263       private Container host;
  264   
  265       // Used by areParallelSiblings, cached to avoid excessive garbage.
  266       private Set<Spring> tmpParallelSet;
  267   
  268       // Indicates Springs have changed in some way since last change.
  269       private boolean springsChanged;
  270   
  271       // Indicates invalidateLayout has been invoked.
  272       private boolean isValid;
  273   
  274       // Whether or not any preferred padding (or container padding) springs
  275       // exist
  276       private boolean hasPreferredPaddingSprings;
  277   
  278       /**
  279        * The LayoutStyle instance to use, if null the sharedInstance is used.
  280        */
  281       private LayoutStyle layoutStyle;
  282   
  283       /**
  284        * If true, components that are not visible are treated as though they
  285        * aren't there.
  286        */
  287       private boolean honorsVisibility;
  288   
  289   
  290       /**
  291        * Enumeration of the possible ways {@code ParallelGroup} can align
  292        * its children.
  293        *
  294        * @see #createParallelGroup(Alignment)
  295        * @since 1.6
  296        */
  297       public enum Alignment {
  298           /**
  299            * Indicates the elements should be
  300            * aligned to the origin.  For the horizontal axis with a left to
  301            * right orientation this means aligned to the left edge. For the
  302            * vertical axis leading means aligned to the top edge.
  303            *
  304            * @see #createParallelGroup(Alignment)
  305            */
  306           LEADING,
  307   
  308           /**
  309            * Indicates the elements should be aligned to the end of the
  310            * region.  For the horizontal axis with a left to right
  311            * orientation this means aligned to the right edge. For the
  312            * vertical axis trailing means aligned to the bottom edge.
  313            *
  314            * @see #createParallelGroup(Alignment)
  315            */
  316           TRAILING,
  317   
  318           /**
  319            * Indicates the elements should be centered in
  320            * the region.
  321            *
  322            * @see #createParallelGroup(Alignment)
  323            */
  324           CENTER,
  325   
  326           /**
  327            * Indicates the elements should be aligned along
  328            * their baseline.
  329            *
  330            * @see #createParallelGroup(Alignment)
  331            * @see #createBaselineGroup(boolean,boolean)
  332            */
  333           BASELINE
  334       }
  335   
  336   
  337       private static void checkSize(int min, int pref, int max,
  338               boolean isComponentSpring) {
  339           checkResizeType(min, isComponentSpring);
  340           if (!isComponentSpring && pref < 0) {
  341               throw new IllegalArgumentException("Pref must be >= 0");
  342           } else if (isComponentSpring) {
  343               checkResizeType(pref, true);
  344           }
  345           checkResizeType(max, isComponentSpring);
  346           checkLessThan(min, pref);
  347           checkLessThan(pref, max);
  348       }
  349   
  350       private static void checkResizeType(int type, boolean isComponentSpring) {
  351           if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE &&
  352                   type != PREFERRED_SIZE) ||
  353                   (!isComponentSpring && type != PREFERRED_SIZE))) {
  354               throw new IllegalArgumentException("Invalid size");
  355           }
  356       }
  357   
  358       private static void checkLessThan(int min, int max) {
  359           if (min >= 0 && max >= 0 && min > max) {
  360               throw new IllegalArgumentException(
  361                       "Following is not met: min<=pref<=max");
  362           }
  363       }
  364   
  365       /**
  366        * Creates a {@code GroupLayout} for the specified {@code Container}.
  367        *
  368        * @param host the {@code Container} the {@code GroupLayout} is
  369        *        the {@code LayoutManager} for
  370        * @throws IllegalArgumentException if host is {@code null}
  371        */
  372       public GroupLayout(Container host) {
  373           if (host == null) {
  374               throw new IllegalArgumentException("Container must be non-null");
  375           }
  376           honorsVisibility = true;
  377           this.host = host;
  378           setHorizontalGroup(createParallelGroup(Alignment.LEADING, true));
  379           setVerticalGroup(createParallelGroup(Alignment.LEADING, true));
  380           componentInfos = new HashMap<Component,ComponentInfo>();
  381           tmpParallelSet = new HashSet<Spring>();
  382       }
  383   
  384       /**
  385        * Sets whether component visiblity is considered when sizing and
  386        * positioning components. A value of {@code true} indicates that
  387        * non-visible components should not be treated as part of the
  388        * layout. A value of {@code false} indicates that components should be
  389        * positioned and sized regardless of visibility.
  390        * <p>
  391        * A value of {@code false} is useful when the visibility of components
  392        * is dynamically adjusted and you don't want surrounding components and
  393        * the sizing to change.
  394        * <p>
  395        * The specified value is used for components that do not have an
  396        * explicit visibility specified.
  397        * <p>
  398        * The default is {@code true}.
  399        *
  400        * @param honorsVisibility whether component visiblity is considered when
  401        *                         sizing and positioning components
  402        * @see #setHonorsVisibility(Component,Boolean)
  403        */
  404       public void setHonorsVisibility(boolean honorsVisibility) {
  405           if (this.honorsVisibility != honorsVisibility) {
  406               this.honorsVisibility = honorsVisibility;
  407               springsChanged = true;
  408               isValid = false;
  409               invalidateHost();
  410           }
  411       }
  412   
  413       /**
  414        * Returns whether component visiblity is considered when sizing and
  415        * positioning components.
  416        *
  417        * @return whether component visiblity is considered when sizing and
  418        *         positioning components
  419        */
  420       public boolean getHonorsVisibility() {
  421           return honorsVisibility;
  422       }
  423   
  424       /**
  425        * Sets whether the component's visiblity is considered for
  426        * sizing and positioning. A value of {@code Boolean.TRUE}
  427        * indicates that if {@code component} is not visible it should
  428        * not be treated as part of the layout. A value of {@code false}
  429        * indicates that {@code component} is positioned and sized
  430        * regardless of it's visibility.  A value of {@code null}
  431        * indicates the value specified by the single argument method {@code
  432        * setHonorsVisibility} should be used.
  433        * <p>
  434        * If {@code component} is not a child of the {@code Container} this
  435        * {@code GroupLayout} is managine, it will be added to the
  436        * {@code Container}.
  437        *
  438        * @param component the component
  439        * @param honorsVisibility whether {@code component}'s visiblity should be
  440        *              considered for sizing and positioning
  441        * @throws IllegalArgumentException if {@code component} is {@code null}
  442        * @see #setHonorsVisibility(Component,Boolean)
  443        */
  444       public void setHonorsVisibility(Component component,
  445               Boolean honorsVisibility) {
  446           if (component == null) {
  447               throw new IllegalArgumentException("Component must be non-null");
  448           }
  449           getComponentInfo(component).setHonorsVisibility(honorsVisibility);
  450           springsChanged = true;
  451           isValid = false;
  452           invalidateHost();
  453       }
  454   
  455       /**
  456        * Sets whether a gap between components should automatically be
  457        * created.  For example, if this is {@code true} and you add two
  458        * components to a {@code SequentialGroup} a gap between the
  459        * two components is automatically be created.  The default is
  460        * {@code false}.
  461        *
  462        * @param autoCreatePadding whether a gap between components is
  463        *        automatically created
  464        */
  465       public void setAutoCreateGaps(boolean autoCreatePadding) {
  466           if (this.autocreatePadding != autoCreatePadding) {
  467               this.autocreatePadding = autoCreatePadding;
  468               invalidateHost();
  469           }
  470       }
  471   
  472       /**
  473        * Returns {@code true} if gaps between components are automatically
  474        * created.
  475        *
  476        * @return {@code true} if gaps between components are automatically
  477        *         created
  478        */
  479       public boolean getAutoCreateGaps() {
  480           return autocreatePadding;
  481       }
  482   
  483       /**
  484        * Sets whether a gap between the container and components that
  485        * touch the border of the container should automatically be
  486        * created. The default is {@code false}.
  487        *
  488        * @param autoCreateContainerPadding whether a gap between the container and
  489        *        components that touch the border of the container should
  490        *        automatically be created
  491        */
  492       public void setAutoCreateContainerGaps(boolean autoCreateContainerPadding){
  493           if (this.autocreateContainerPadding != autoCreateContainerPadding) {
  494               this.autocreateContainerPadding = autoCreateContainerPadding;
  495               horizontalGroup = createTopLevelGroup(getHorizontalGroup());
  496               verticalGroup = createTopLevelGroup(getVerticalGroup());
  497               invalidateHost();
  498           }
  499       }
  500   
  501       /**
  502        * Returns {@code true} if gaps between the container and components that
  503        * border the container are automatically created.
  504        *
  505        * @return {@code true} if gaps between the container and components that
  506        *         border the container are automatically created
  507        */
  508       public boolean getAutoCreateContainerGaps() {
  509           return autocreateContainerPadding;
  510       }
  511   
  512       /**
  513        * Sets the {@code Group} that positions and sizes
  514        * components along the horizontal axis.
  515        *
  516        * @param group the {@code Group} that positions and sizes
  517        *        components along the horizontal axis
  518        * @throws IllegalArgumentException if group is {@code null}
  519        */
  520       public void setHorizontalGroup(Group group) {
  521           if (group == null) {
  522               throw new IllegalArgumentException("Group must be non-null");
  523           }
  524           horizontalGroup = createTopLevelGroup(group);
  525           invalidateHost();
  526       }
  527   
  528       /**
  529        * Returns the {@code Group} that positions and sizes components
  530        * along the horizontal axis.
  531        *
  532        * @return the {@code Group} responsible for positioning and
  533        *         sizing component along the horizontal axis
  534        */
  535       private Group getHorizontalGroup() {
  536           int index = 0;
  537           if (horizontalGroup.springs.size() > 1) {
  538               index = 1;
  539           }
  540           return (Group)horizontalGroup.springs.get(index);
  541       }
  542   
  543       /**
  544        * Sets the {@code Group} that positions and sizes
  545        * components along the vertical axis.
  546        *
  547        * @param group the {@code Group} that positions and sizes
  548        *        components along the vertical axis
  549        * @throws IllegalArgumentException if group is {@code null}
  550        */
  551       public void setVerticalGroup(Group group) {
  552           if (group == null) {
  553               throw new IllegalArgumentException("Group must be non-null");
  554           }
  555           verticalGroup = createTopLevelGroup(group);
  556           invalidateHost();
  557       }
  558   
  559       /**
  560        * Returns the {@code Group} that positions and sizes components
  561        * along the vertical axis.
  562        *
  563        * @return the {@code Group} responsible for positioning and
  564        *         sizing component along the vertical axis
  565        */
  566       private Group getVerticalGroup() {
  567           int index = 0;
  568           if (verticalGroup.springs.size() > 1) {
  569               index = 1;
  570           }
  571           return (Group)verticalGroup.springs.get(index);
  572       }
  573   
  574       /**
  575        * Wraps the user specified group in a sequential group.  If
  576        * container gaps should be generated the necessary springs are
  577        * added.
  578        */
  579       private Group createTopLevelGroup(Group specifiedGroup) {
  580           SequentialGroup group = createSequentialGroup();
  581           if (getAutoCreateContainerGaps()) {
  582               group.addSpring(new ContainerAutoPreferredGapSpring());
  583               group.addGroup(specifiedGroup);
  584               group.addSpring(new ContainerAutoPreferredGapSpring());
  585           } else {
  586               group.addGroup(specifiedGroup);
  587           }
  588           return group;
  589       }
  590   
  591       /**
  592        * Creates and returns a {@code SequentialGroup}.
  593        *
  594        * @return a new {@code SequentialGroup}
  595        */
  596       public SequentialGroup createSequentialGroup() {
  597           return new SequentialGroup();
  598       }
  599   
  600       /**
  601        * Creates and returns a {@code ParallelGroup} with an alignment of
  602        * {@code Alignment.LEADING}.  This is a cover method for the more
  603        * general {@code createParallelGroup(Alignment)} method.
  604        *
  605        * @return a new {@code ParallelGroup}
  606        * @see #createParallelGroup(Alignment)
  607        */
  608       public ParallelGroup createParallelGroup() {
  609           return createParallelGroup(Alignment.LEADING);
  610       }
  611   
  612       /**
  613        * Creates and returns a {@code ParallelGroup} with the specified
  614        * alignment.  This is a cover method for the more general {@code
  615        * createParallelGroup(Alignment,boolean)} method with {@code true}
  616        * supplied for the second argument.
  617        *
  618        * @param alignment the alignment for the elements of the group
  619        * @throws IllegalArgumentException if {@code alignment} is {@code null}
  620        * @return a new {@code ParallelGroup}
  621        * @see #createBaselineGroup
  622        * @see ParallelGroup
  623        */
  624       public ParallelGroup createParallelGroup(Alignment alignment) {
  625           return createParallelGroup(alignment, true);
  626       }
  627   
  628       /**
  629        * Creates and returns a {@code ParallelGroup} with the specified
  630        * alignment and resize behavior. The {@code
  631        * alignment} argument specifies how children elements are
  632        * positioned that do not fill the group. For example, if a {@code
  633        * ParallelGroup} with an alignment of {@code TRAILING} is given
  634        * 100 and a child only needs 50, the child is
  635        * positioned at the position 50 (with a component orientation of
  636        * left-to-right).
  637        * <p>
  638        * Baseline alignment is only useful when used along the vertical
  639        * axis. A {@code ParallelGroup} created with a baseline alignment
  640        * along the horizontal axis is treated as {@code LEADING}.
  641        * <p>
  642        * Refer to {@link GroupLayout.ParallelGroup ParallelGroup} for details on
  643        * the behavior of baseline groups.
  644        *
  645        * @param alignment the alignment for the elements of the group
  646        * @param resizable {@code true} if the group is resizable; if the group
  647        *        is not resizable the preferred size is used for the
  648        *        minimum and maximum size of the group
  649        * @throws IllegalArgumentException if {@code alignment} is {@code null}
  650        * @return a new {@code ParallelGroup}
  651        * @see #createBaselineGroup
  652        * @see GroupLayout.ParallelGroup
  653        */
  654       public ParallelGroup createParallelGroup(Alignment alignment,
  655               boolean resizable){
  656           if (alignment == null) {
  657               throw new IllegalArgumentException("alignment must be non null");
  658           }
  659   
  660           if (alignment == Alignment.BASELINE) {
  661               return new BaselineGroup(resizable);
  662           }
  663           return new ParallelGroup(alignment, resizable);
  664       }
  665   
  666       /**
  667        * Creates and returns a {@code ParallelGroup} that aligns it's
  668        * elements along the baseline.
  669        *
  670        * @param resizable whether the group is resizable
  671        * @param anchorBaselineToTop whether the baseline is anchored to
  672        *        the top or bottom of the group
  673        * @see #createBaselineGroup
  674        * @see ParallelGroup
  675        */
  676       public ParallelGroup createBaselineGroup(boolean resizable,
  677               boolean anchorBaselineToTop) {
  678           return new BaselineGroup(resizable, anchorBaselineToTop);
  679       }
  680   
  681       /**
  682        * Forces the specified components to have the same size
  683        * regardless of their preferred, minimum or maximum sizes. Components that
  684        * are linked are given the maximum of the preferred size of each of
  685        * the linked components. For example, if you link two components with
  686        * a preferred width of 10 and 20, both components are given a width of 20.
  687        * <p>
  688        * This can be used multiple times to force any number of
  689        * components to share the same size.
  690        * <p>
  691        * Linked Components are not be resizable.
  692        *
  693        * @param components the {@code Component}s that are to have the same size
  694        * @throws IllegalArgumentException if {@code components} is
  695        *         {@code null}, or contains {@code null}
  696        * @see #linkSize(int,Component[])
  697        */
  698       public void linkSize(Component... components) {
  699           linkSize(SwingConstants.HORIZONTAL, components);
  700           linkSize(SwingConstants.VERTICAL, components);
  701       }
  702   
  703       /**
  704        * Forces the specified components to have the same size along the
  705        * specified axis regardless of their preferred, minimum or
  706        * maximum sizes. Components that are linked are given the maximum
  707        * of the preferred size of each of the linked components. For
  708        * example, if you link two components along the horizontal axis
  709        * and the preferred width is 10 and 20, both components are given
  710        * a width of 20.
  711        * <p>
  712        * This can be used multiple times to force any number of
  713        * components to share the same size.
  714        * <p>
  715        * Linked {@code Component}s are not be resizable.
  716        *
  717        * @param components the {@code Component}s that are to have the same size
  718        * @param axis the axis to link the size along; one of
  719        *             {@code SwingConstants.HORIZONTAL} or
  720        *             {@code SwingConstans.VERTICAL}
  721        * @throws IllegalArgumentException if {@code components} is
  722        *         {@code null}, or contains {@code null}; or {@code axis}
  723        *          is not {@code SwingConstants.HORIZONTAL} or
  724        *          {@code SwingConstants.VERTICAL}
  725        */
  726       public void linkSize(int axis, Component... components) {
  727           if (components == null) {
  728               throw new IllegalArgumentException("Components must be non-null");
  729           }
  730           for (int counter = components.length - 1; counter >= 0; counter--) {
  731               Component c = components[counter];
  732               if (components[counter] == null) {
  733                   throw new IllegalArgumentException(
  734                           "Components must be non-null");
  735               }
  736               // Force the component to be added
  737               getComponentInfo(c);
  738           }
  739           int glAxis;
  740           if (axis == SwingConstants.HORIZONTAL) {
  741               glAxis = HORIZONTAL;
  742           } else if (axis == SwingConstants.VERTICAL) {
  743               glAxis = VERTICAL;
  744           } else {
  745               throw new IllegalArgumentException("Axis must be one of " +
  746                       "SwingConstants.HORIZONTAL or SwingConstants.VERTICAL");
  747           }
  748           LinkInfo master = getComponentInfo(
  749                   components[components.length - 1]).getLinkInfo(glAxis);
  750           for (int counter = components.length - 2; counter >= 0; counter--) {
  751               master.add(getComponentInfo(components[counter]));
  752           }
  753           invalidateHost();
  754       }
  755   
  756       /**
  757        * Replaces an existing component with a new one.
  758        *
  759        * @param existingComponent the component that should be removed
  760        *        and replaced with {@code newComponent}
  761        * @param newComponent the component to put in
  762        *        {@code existingComponent}'s place
  763        * @throws IllegalArgumentException if either of the components are
  764        *         {@code null} or {@code existingComponent} is not being managed
  765        *         by this layout manager
  766        */
  767       public void replace(Component existingComponent, Component newComponent) {
  768           if (existingComponent == null || newComponent == null) {
  769               throw new IllegalArgumentException("Components must be non-null");
  770           }
  771           // Make sure all the components have been registered, otherwise we may
  772           // not update the correct Springs.
  773           if (springsChanged) {
  774               registerComponents(horizontalGroup, HORIZONTAL);
  775               registerComponents(verticalGroup, VERTICAL);
  776           }
  777           ComponentInfo info = componentInfos.remove(existingComponent);
  778           if (info == null) {
  779               throw new IllegalArgumentException("Component must already exist");
  780           }
  781           host.remove(existingComponent);
  782           if (newComponent.getParent() != host) {
  783               host.add(newComponent);
  784           }
  785           info.setComponent(newComponent);
  786           componentInfos.put(newComponent, info);
  787           invalidateHost();
  788       }
  789   
  790       /**
  791        * Sets the {@code LayoutStyle} used to calculate the preferred
  792        * gaps between components. A value of {@code null} indicates the
  793        * shared instance of {@code LayoutStyle} should be used.
  794        *
  795        * @param layoutStyle the {@code LayoutStyle} to use
  796        * @see LayoutStyle
  797        */
  798       public void setLayoutStyle(LayoutStyle layoutStyle) {
  799           this.layoutStyle = layoutStyle;
  800           invalidateHost();
  801       }
  802   
  803       /**
  804        * Returns the {@code LayoutStyle} used for calculating the preferred
  805        * gap between components. This returns the value specified to
  806        * {@code setLayoutStyle}, which may be {@code null}.
  807        *
  808        * @return the {@code LayoutStyle} used for calculating the preferred
  809        *         gap between components
  810        */
  811       public LayoutStyle getLayoutStyle() {
  812           return layoutStyle;
  813       }
  814   
  815       private LayoutStyle getLayoutStyle0() {
  816           LayoutStyle layoutStyle = getLayoutStyle();
  817           if (layoutStyle == null) {
  818               layoutStyle = LayoutStyle.getInstance();
  819           }
  820           return layoutStyle;
  821       }
  822   
  823       private void invalidateHost() {
  824           if (host instanceof JComponent) {
  825               ((JComponent)host).revalidate();
  826           } else {
  827               host.invalidate();
  828           }
  829           host.repaint();
  830       }
  831   
  832       //
  833       // LayoutManager
  834       //
  835       /**
  836        * Notification that a {@code Component} has been added to
  837        * the parent container.  You should not invoke this method
  838        * directly, instead you should use one of the {@code Group}
  839        * methods to add a {@code Component}.
  840        *
  841        * @param name the string to be associated with the component
  842        * @param component the {@code Component} to be added
  843        */
  844       public void addLayoutComponent(String name, Component component) {
  845       }
  846   
  847       /**
  848        * Notification that a {@code Component} has been removed from
  849        * the parent container.  You should not invoke this method
  850        * directly, instead invoke {@code remove} on the parent
  851        * {@code Container}.
  852        *
  853        * @param component the component to be removed
  854        * @see java.awt.Component#remove
  855        */
  856       public void removeLayoutComponent(Component component) {
  857           ComponentInfo info = componentInfos.remove(component);
  858           if (info != null) {
  859               info.dispose();
  860               springsChanged = true;
  861               isValid = false;
  862           }
  863       }
  864   
  865       /**
  866        * Returns the preferred size for the specified container.
  867        *
  868        * @param parent the container to return the preferred size for
  869        * @return the preferred size for {@code parent}
  870        * @throws IllegalArgumentException if {@code parent} is not
  871        *         the same {@code Container} this was created with
  872        * @throws IllegalStateException if any of the components added to
  873        *         this layout are not in both a horizontal and vertical group
  874        * @see java.awt.Container#getPreferredSize
  875        */
  876       public Dimension preferredLayoutSize(Container parent) {
  877           checkParent(parent);
  878           prepare(PREF_SIZE);
  879           return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),
  880                   verticalGroup.getPreferredSize(VERTICAL));
  881       }
  882   
  883       /**
  884        * Returns the minimum size for the specified container.
  885        *
  886        * @param parent the container to return the size for
  887        * @return the minimum size for {@code parent}
  888        * @throws IllegalArgumentException if {@code parent} is not
  889        *         the same {@code Container} that this was created with
  890        * @throws IllegalStateException if any of the components added to
  891        *         this layout are not in both a horizontal and vertical group
  892        * @see java.awt.Container#getMinimumSize
  893        */
  894       public Dimension minimumLayoutSize(Container parent) {
  895           checkParent(parent);
  896           prepare(MIN_SIZE);
  897           return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),
  898                   verticalGroup.getMinimumSize(VERTICAL));
  899       }
  900   
  901       /**
  902        * Lays out the specified container.
  903        *
  904        * @param parent the container to be laid out
  905        * @throws IllegalStateException if any of the components added to
  906        *         this layout are not in both a horizontal and vertical group
  907        */
  908       public void layoutContainer(Container parent) {
  909           // Step 1: Prepare for layout.
  910           prepare(SPECIFIC_SIZE);
  911           Insets insets = parent.getInsets();
  912           int width = parent.getWidth() - insets.left - insets.right;
  913           int height = parent.getHeight() - insets.top - insets.bottom;
  914           boolean ltr = isLeftToRight();
  915           if (getAutoCreateGaps() || getAutoCreateContainerGaps() ||
  916                   hasPreferredPaddingSprings) {
  917               // Step 2: Calculate autopadding springs
  918               calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0,
  919                       width);
  920               calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0,
  921                       height);
  922           }
  923           // Step 3: set the size of the groups.
  924           horizontalGroup.setSize(HORIZONTAL, 0, width);
  925           verticalGroup.setSize(VERTICAL, 0, height);
  926           // Step 4: apply the size to the components.
  927           for (ComponentInfo info : componentInfos.values()) {
  928               info.setBounds(insets, width, ltr);
  929           }
  930       }
  931   
  932       //
  933       // LayoutManager2
  934       //
  935       /**
  936        * Notification that a {@code Component} has been added to
  937        * the parent container.  You should not invoke this method
  938        * directly, instead you should use one of the {@code Group}
  939        * methods to add a {@code Component}.
  940        *
  941        * @param component the component added
  942        * @param constraints description of where to place the component
  943        */
  944       public void addLayoutComponent(Component component, Object constraints) {
  945       }
  946   
  947       /**
  948        * Returns the maximum size for the specified container.
  949        *
  950        * @param parent the container to return the size for
  951        * @return the maximum size for {@code parent}
  952        * @throws IllegalArgumentException if {@code parent} is not
  953        *         the same {@code Container} that this was created with
  954        * @throws IllegalStateException if any of the components added to
  955        *         this layout are not in both a horizontal and vertical group
  956        * @see java.awt.Container#getMaximumSize
  957        */
  958       public Dimension maximumLayoutSize(Container parent) {
  959           checkParent(parent);
  960           prepare(MAX_SIZE);
  961           return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),
  962                   verticalGroup.getMaximumSize(VERTICAL));
  963       }
  964   
  965       /**
  966        * Returns the alignment along the x axis.  This specifies how
  967        * the component would like to be aligned relative to other
  968        * components.  The value should be a number between 0 and 1
  969        * where 0 represents alignment along the origin, 1 is aligned
  970        * the furthest away from the origin, 0.5 is centered, etc.
  971        *
  972        * @param parent the {@code Container} hosting this {@code LayoutManager}
  973        * @throws IllegalArgumentException if {@code parent} is not
  974        *         the same {@code Container} that this was created with
  975        * @return the alignment; this implementation returns {@code .5}
  976        */
  977       public float getLayoutAlignmentX(Container parent) {
  978           checkParent(parent);
  979           return .5f;
  980       }
  981   
  982       /**
  983        * Returns the alignment along the y axis.  This specifies how
  984        * the component would like to be aligned relative to other
  985        * components.  The value should be a number between 0 and 1
  986        * where 0 represents alignment along the origin, 1 is aligned
  987        * the furthest away from the origin, 0.5 is centered, etc.
  988        *
  989        * @param parent the {@code Container} hosting this {@code LayoutManager}
  990        * @throws IllegalArgumentException if {@code parent} is not
  991        *         the same {@code Container} that this was created with
  992        * @return alignment; this implementation returns {@code .5}
  993        */
  994       public float getLayoutAlignmentY(Container parent) {
  995           checkParent(parent);
  996           return .5f;
  997       }
  998   
  999       /**
 1000        * Invalidates the layout, indicating that if the layout manager
 1001        * has cached information it should be discarded.
 1002        *
 1003        * @param parent the {@code Container} hosting this LayoutManager
 1004        * @throws IllegalArgumentException if {@code parent} is not
 1005        *         the same {@code Container} that this was created with
 1006        */
 1007       public void invalidateLayout(Container parent) {
 1008           checkParent(parent);
 1009           // invalidateLayout is called from Container.invalidate, which
 1010           // does NOT grab the treelock.  All other methods do.  To make sure
 1011           // there aren't any possible threading problems we grab the tree lock
 1012           // here.
 1013           synchronized(parent.getTreeLock()) {
 1014               isValid = false;
 1015           }
 1016       }
 1017   
 1018       private void prepare(int sizeType) {
 1019           boolean visChanged = false;
 1020           // Step 1: If not-valid, clear springs and update visibility.
 1021           if (!isValid) {
 1022               isValid = true;
 1023               horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);
 1024               verticalGroup.setSize(VERTICAL, UNSET, UNSET);
 1025               for (ComponentInfo ci : componentInfos.values()) {
 1026                   if (ci.updateVisibility()) {
 1027                       visChanged = true;
 1028                   }
 1029                   ci.clearCachedSize();
 1030               }
 1031           }
 1032           // Step 2: Make sure components are bound to ComponentInfos
 1033           if (springsChanged) {
 1034               registerComponents(horizontalGroup, HORIZONTAL);
 1035               registerComponents(verticalGroup, VERTICAL);
 1036           }
 1037           // Step 3: Adjust the autopadding. This removes existing
 1038           // autopadding, then recalculates where it should go.
 1039           if (springsChanged || visChanged) {
 1040               checkComponents();
 1041               horizontalGroup.removeAutopadding();
 1042               verticalGroup.removeAutopadding();
 1043               if (getAutoCreateGaps()) {
 1044                   insertAutopadding(true);
 1045               } else if (hasPreferredPaddingSprings ||
 1046                       getAutoCreateContainerGaps()) {
 1047                   insertAutopadding(false);
 1048               }
 1049               springsChanged = false;
 1050           }
 1051           // Step 4: (for min/pref/max size calculations only) calculate the
 1052           // autopadding. This invokes for unsetting the calculated values, then
 1053           // recalculating them.
 1054           // If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this
 1055           // step will be done later on.
 1056           if (sizeType != SPECIFIC_SIZE && (getAutoCreateGaps() ||
 1057                   getAutoCreateContainerGaps() || hasPreferredPaddingSprings)) {
 1058               calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0);
 1059               calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);
 1060           }
 1061       }
 1062   
 1063       private void calculateAutopadding(Group group, int axis, int sizeType,
 1064               int origin, int size) {
 1065           group.unsetAutopadding();
 1066           switch(sizeType) {
 1067               case MIN_SIZE:
 1068                   size = group.getMinimumSize(axis);
 1069                   break;
 1070               case PREF_SIZE:
 1071                   size = group.getPreferredSize(axis);
 1072                   break;
 1073               case MAX_SIZE:
 1074                   size = group.getMaximumSize(axis);
 1075                   break;
 1076               default:
 1077                   break;
 1078           }
 1079           group.setSize(axis, origin, size);
 1080           group.calculateAutopadding(axis);
 1081       }
 1082   
 1083       private void checkComponents() {
 1084           for (ComponentInfo info : componentInfos.values()) {
 1085               if (info.horizontalSpring == null) {
 1086                   throw new IllegalStateException(info.component +
 1087                           " is not attached to a horizontal group");
 1088               }
 1089               if (info.verticalSpring == null) {
 1090                   throw new IllegalStateException(info.component +
 1091                           " is not attached to a vertical group");
 1092               }
 1093           }
 1094       }
 1095   
 1096       private void registerComponents(Group group, int axis) {
 1097           List<Spring> springs = group.springs;
 1098           for (int counter = springs.size() - 1; counter >= 0; counter--) {
 1099               Spring spring = springs.get(counter);
 1100               if (spring instanceof ComponentSpring) {
 1101                   ((ComponentSpring)spring).installIfNecessary(axis);
 1102               } else if (spring instanceof Group) {
 1103                   registerComponents((Group)spring, axis);
 1104               }
 1105           }
 1106       }
 1107   
 1108       private Dimension adjustSize(int width, int height) {
 1109           Insets insets = host.getInsets();
 1110           return new Dimension(width + insets.left + insets.right,
 1111                   height + insets.top + insets.bottom);
 1112       }
 1113   
 1114       private void checkParent(Container parent) {
 1115           if (parent != host) {
 1116               throw new IllegalArgumentException(
 1117                       "GroupLayout can only be used with one Container at a time");
 1118           }
 1119       }
 1120   
 1121       /**
 1122        * Returns the {@code ComponentInfo} for the specified Component,
 1123        * creating one if necessary.
 1124        */
 1125       private ComponentInfo getComponentInfo(Component component) {
 1126           ComponentInfo info = componentInfos.get(component);
 1127           if (info == null) {
 1128               info = new ComponentInfo(component);
 1129               componentInfos.put(component, info);
 1130               if (component.getParent() != host) {
 1131                   host.add(component);
 1132               }
 1133           }
 1134           return info;
 1135       }
 1136   
 1137       /**
 1138        * Adjusts the autopadding springs for the horizontal and vertical
 1139        * groups.  If {@code insert} is {@code true} this will insert auto padding
 1140        * springs, otherwise this will only adjust the springs that
 1141        * comprise auto preferred padding springs.
 1142        */
 1143       private void insertAutopadding(boolean insert) {
 1144           horizontalGroup.insertAutopadding(HORIZONTAL,
 1145                   new ArrayList<AutoPreferredGapSpring>(1),
 1146                   new ArrayList<AutoPreferredGapSpring>(1),
 1147                   new ArrayList<ComponentSpring>(1),
 1148                   new ArrayList<ComponentSpring>(1), insert);
 1149           verticalGroup.insertAutopadding(VERTICAL,
 1150                   new ArrayList<AutoPreferredGapSpring>(1),
 1151                   new ArrayList<AutoPreferredGapSpring>(1),
 1152                   new ArrayList<ComponentSpring>(1),
 1153                   new ArrayList<ComponentSpring>(1), insert);
 1154       }
 1155   
 1156       /**
 1157        * Returns {@code true} if the two Components have a common ParallelGroup
 1158        * ancestor along the particular axis.
 1159        */
 1160       private boolean areParallelSiblings(Component source, Component target,
 1161               int axis) {
 1162           ComponentInfo sourceInfo = getComponentInfo(source);
 1163           ComponentInfo targetInfo = getComponentInfo(target);
 1164           Spring sourceSpring;
 1165           Spring targetSpring;
 1166           if (axis == HORIZONTAL) {
 1167               sourceSpring = sourceInfo.horizontalSpring;
 1168               targetSpring = targetInfo.horizontalSpring;
 1169           } else {
 1170               sourceSpring = sourceInfo.verticalSpring;
 1171               targetSpring = targetInfo.verticalSpring;
 1172           }
 1173           Set<Spring> sourcePath = tmpParallelSet;
 1174           sourcePath.clear();
 1175           Spring spring = sourceSpring.getParent();
 1176           while (spring != null) {
 1177               sourcePath.add(spring);
 1178               spring = spring.getParent();
 1179           }
 1180           spring = targetSpring.getParent();
 1181           while (spring != null) {
 1182               if (sourcePath.contains(spring)) {
 1183                   sourcePath.clear();
 1184                   while (spring != null) {
 1185                       if (spring instanceof ParallelGroup) {
 1186                           return true;
 1187                       }
 1188                       spring = spring.getParent();
 1189                   }
 1190                   return false;
 1191               }
 1192               spring = spring.getParent();
 1193           }
 1194           sourcePath.clear();
 1195           return false;
 1196       }
 1197   
 1198       private boolean isLeftToRight() {
 1199           return host.getComponentOrientation().isLeftToRight();
 1200       }
 1201   
 1202       /**
 1203        * Returns a string representation of this {@code GroupLayout}.
 1204        * This method is intended to be used for debugging purposes,
 1205        * and the content and format of the returned string may vary
 1206        * between implementations.
 1207        *
 1208        * @return a string representation of this {@code GroupLayout}
 1209        **/
 1210       public String toString() {
 1211           if (springsChanged) {
 1212               registerComponents(horizontalGroup, HORIZONTAL);
 1213               registerComponents(verticalGroup, VERTICAL);
 1214           }
 1215           StringBuffer buffer = new StringBuffer();
 1216           buffer.append("HORIZONTAL\n");
 1217           createSpringDescription(buffer, horizontalGroup, "  ", HORIZONTAL);
 1218           buffer.append("\nVERTICAL\n");
 1219           createSpringDescription(buffer, verticalGroup, "  ", VERTICAL);
 1220           return buffer.toString();
 1221       }
 1222   
 1223       private void createSpringDescription(StringBuffer buffer, Spring spring,
 1224               String indent, int axis) {
 1225           String origin = "";
 1226           String padding = "";
 1227           if (spring instanceof ComponentSpring) {
 1228               ComponentSpring cSpring = (ComponentSpring)spring;
 1229               origin = Integer.toString(cSpring.getOrigin()) + " ";
 1230               String name = cSpring.getComponent().getName();
 1231               if (name != null) {
 1232                   origin = "name=" + name + ", ";
 1233               }
 1234           }
 1235           if (spring instanceof AutoPreferredGapSpring) {
 1236               AutoPreferredGapSpring paddingSpring =
 1237                       (AutoPreferredGapSpring)spring;
 1238               padding = ", userCreated=" + paddingSpring.getUserCreated() +
 1239                       ", matches=" + paddingSpring.getMatchDescription();
 1240           }
 1241           buffer.append(indent + spring.getClass().getName() + " " +
 1242                   Integer.toHexString(spring.hashCode()) + " " +
 1243                   origin +
 1244                   ", size=" + spring.getSize() +
 1245                   ", alignment=" + spring.getAlignment() +
 1246                   " prefs=[" + spring.getMinimumSize(axis) +
 1247                   " " + spring.getPreferredSize(axis) +
 1248                   " " + spring.getMaximumSize(axis) +
 1249                   padding + "]\n");
 1250           if (spring instanceof Group) {
 1251               List<Spring> springs = ((Group)spring).springs;
 1252               indent += "  ";
 1253               for (int counter = 0; counter < springs.size(); counter++) {
 1254                   createSpringDescription(buffer, springs.get(counter), indent,
 1255                           axis);
 1256               }
 1257           }
 1258       }
 1259   
 1260   
 1261       /**
 1262        * Spring consists of a range: min, pref and max, a value some where in
 1263        * the middle of that, and a location. Spring caches the
 1264        * min/max/pref.  If the min/pref/max has internally changes, or needs
 1265        * to be updated you must invoke clear.
 1266        */
 1267       private abstract class Spring {
 1268           private int size;
 1269           private int min;
 1270           private int max;
 1271           private int pref;
 1272           private Spring parent;
 1273   
 1274           private Alignment alignment;
 1275   
 1276           Spring() {
 1277               min = pref = max = UNSET;
 1278           }
 1279   
 1280           /**
 1281            * Calculates and returns the minimum size.
 1282            *
 1283            * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
 1284            * @return the minimum size
 1285            */
 1286           abstract int calculateMinimumSize(int axis);
 1287   
 1288           /**
 1289            * Calculates and returns the preferred size.
 1290            *
 1291            * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
 1292            * @return the preferred size
 1293            */
 1294           abstract int calculatePreferredSize(int axis);
 1295   
 1296           /**
 1297            * Calculates and returns the minimum size.
 1298            *
 1299            * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
 1300            * @return the minimum size
 1301            */
 1302           abstract int calculateMaximumSize(int axis);
 1303   
 1304           /**
 1305            * Sets the parent of this Spring.
 1306            */
 1307           void setParent(Spring parent) {
 1308               this.parent = parent;
 1309           }
 1310   
 1311           /**
 1312            * Returns the parent of this spring.
 1313            */
 1314           Spring getParent() {
 1315               return parent;
 1316           }
 1317   
 1318           // This is here purely as a conveniance for ParallelGroup to avoid
 1319           // having to track alignment separately.
 1320           void setAlignment(Alignment alignment) {
 1321               this.alignment = alignment;
 1322           }
 1323   
 1324           /**
 1325            * Alignment for this Spring, this may be null.
 1326            */
 1327           Alignment getAlignment() {
 1328               return alignment;
 1329           }
 1330   
 1331           /**
 1332            * Returns the minimum size.
 1333            */
 1334           final int getMinimumSize(int axis) {
 1335               if (min == UNSET) {
 1336                   min = constrain(calculateMinimumSize(axis));
 1337               }
 1338               return min;
 1339           }
 1340   
 1341           /**
 1342            * Returns the preferred size.
 1343            */
 1344           final int getPreferredSize(int axis) {
 1345               if (pref == UNSET) {
 1346                   pref = constrain(calculatePreferredSize(axis));
 1347               }
 1348               return pref;
 1349           }
 1350   
 1351           /**
 1352            * Returns the maximum size.
 1353            */
 1354           final int getMaximumSize(int axis) {
 1355               if (max == UNSET) {
 1356                   max = constrain(calculateMaximumSize(axis));
 1357               }
 1358               return max;
 1359           }
 1360   
 1361           /**
 1362            * Sets the value and location of the spring.  Subclasses
 1363            * will want to invoke super, then do any additional sizing.
 1364            *
 1365            * @param axis HORIZONTAL or VERTICAL
 1366            * @param origin of this Spring
 1367            * @param size of the Spring.  If size is UNSET, this invokes
 1368            *        clear.
 1369            */
 1370           void setSize(int axis, int origin, int size) {
 1371               this.size = size;
 1372               if (size == UNSET) {
 1373                   unset();
 1374               }
 1375           }
 1376   
 1377           /**
 1378            * Resets the cached min/max/pref.
 1379            */
 1380           void unset() {
 1381               size = min = pref = max = UNSET;
 1382           }
 1383   
 1384           /**
 1385            * Returns the current size.
 1386            */
 1387           int getSize() {
 1388               return size;
 1389           }
 1390   
 1391           int constrain(int value) {
 1392               return Math.min(value, Short.MAX_VALUE);
 1393           }
 1394   
 1395           int getBaseline() {
 1396               return -1;
 1397           }
 1398   
 1399           BaselineResizeBehavior getBaselineResizeBehavior() {
 1400               return BaselineResizeBehavior.OTHER;
 1401           }
 1402   
 1403           final boolean isResizable(int axis) {
 1404               int min = getMinimumSize(axis);
 1405               int pref = getPreferredSize(axis);
 1406               return (min != pref || pref != getMaximumSize(axis));
 1407           }
 1408   
 1409           /**
 1410            * Returns {@code true} if this spring will ALWAYS have a zero
 1411            * size. This should NOT check the current size, rather it's
 1412            * meant to quickly test if this Spring will always have a
 1413            * zero size.
 1414            *
 1415            * @param treatAutopaddingAsZeroSized if {@code true}, auto padding
 1416            *        springs should be treated as having a size of {@code 0}
 1417            * @return {@code true} if this spring will have a zero size,
 1418            *         {@code false} otherwise
 1419            */
 1420           abstract boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized);
 1421       }
 1422   
 1423       /**
 1424        * {@code Group} provides the basis for the two types of
 1425        * operations supported by {@code GroupLayout}: laying out
 1426        * components one after another ({@link SequentialGroup SequentialGroup})
 1427        * or aligned ({@link ParallelGroup ParallelGroup}). {@code Group} and
 1428        * its subclasses have no public constructor; to create one use
 1429        * one of {@code createSequentialGroup} or
 1430        * {@code createParallelGroup}. Additionally, taking a {@code Group}
 1431        * created from one {@code GroupLayout} and using it with another
 1432        * will produce undefined results.
 1433        * <p>
 1434        * Various methods in {@code Group} and its subclasses allow you
 1435        * to explicitly specify the range. The arguments to these methods
 1436        * can take two forms, either a value greater than or equal to 0,
 1437        * or one of {@code DEFAULT_SIZE} or {@code PREFERRED_SIZE}. A
 1438        * value greater than or equal to {@code 0} indicates a specific
 1439        * size. {@code DEFAULT_SIZE} indicates the corresponding size
 1440        * from the component should be used.  For example, if {@code
 1441        * DEFAULT_SIZE} is passed as the minimum size argument, the
 1442        * minimum size is obtained from invoking {@code getMinimumSize}
 1443        * on the component. Likewise, {@code PREFERRED_SIZE} indicates
 1444        * the value from {@code getPreferredSize} should be used.
 1445        * The following example adds {@code myComponent} to {@code group}
 1446        * with specific values for the range. That is, the minimum is
 1447        * explicitly specified as 100, preferred as 200, and maximum as
 1448        * 300.
 1449        * <pre>
 1450        *   group.addComponent(myComponent, 100, 200, 300);
 1451        * </pre>
 1452        * The following example adds {@code myComponent} to {@code group} using
 1453        * a combination of the forms. The minimum size is forced to be the
 1454        * same as the preferred size, the preferred size is determined by
 1455        * using {@code myComponent.getPreferredSize} and the maximum is
 1456        * determined by invoking {@code getMaximumSize} on the component.
 1457        * <pre>
 1458        *   group.addComponent(myComponent, GroupLayout.PREFERRED_SIZE,
 1459        *             GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE);
 1460        * </pre>
 1461        * <p>
 1462        * Unless otherwise specified all the methods of {@code Group} and
 1463        * its subclasses that allow you to specify a range throw an
 1464        * {@code IllegalArgumentException} if passed an invalid range. An
 1465        * invalid range is one in which any of the values are &lt; 0 and
 1466        * not one of {@code PREFERRED_SIZE} or {@code DEFAULT_SIZE}, or
 1467        * the following is not met (for specific values): {@code min}
 1468        * &lt;= {@code pref} &lt;= {@code max}.
 1469        * <p>
 1470        * Similarly any methods that take a {@code Component} throw a
 1471        * {@code IllegalArgumentException} if passed {@code null} and any methods
 1472        * that take a {@code Group} throw an {@code NullPointerException} if
 1473        * passed {@code null}.
 1474        *
 1475        * @see #createSequentialGroup
 1476        * @see #createParallelGroup
 1477        * @since 1.6
 1478        */
 1479       public abstract class Group extends Spring {
 1480           // private int origin;
 1481           // private int size;
 1482           List<Spring> springs;
 1483   
 1484           Group() {
 1485               springs = new ArrayList<Spring>();
 1486           }
 1487   
 1488           /**
 1489            * Adds a {@code Group} to this {@code Group}.
 1490            *
 1491            * @param group the {@code Group} to add
 1492            * @return this {@code Group}
 1493            */
 1494           public Group addGroup(Group group) {
 1495               return addSpring(group);
 1496           }
 1497   
 1498           /**
 1499            * Adds a {@code Component} to this {@code Group}.
 1500            *
 1501            * @param component the {@code Component} to add
 1502            * @return this {@code Group}
 1503            */
 1504           public Group addComponent(Component component) {
 1505               return addComponent(component, DEFAULT_SIZE, DEFAULT_SIZE,
 1506                       DEFAULT_SIZE);
 1507           }
 1508   
 1509           /**
 1510            * Adds a {@code Component} to this {@code Group}
 1511            * with the specified size.
 1512            *
 1513            * @param component the {@code Component} to add
 1514            * @param min the minimum size or one of {@code DEFAULT_SIZE} or
 1515            *            {@code PREFERRED_SIZE}
 1516            * @param pref the preferred size or one of {@code DEFAULT_SIZE} or
 1517            *            {@code PREFERRED_SIZE}
 1518            * @param max the maximum size or one of {@code DEFAULT_SIZE} or
 1519            *            {@code PREFERRED_SIZE}
 1520            * @return this {@code Group}
 1521            */
 1522           public Group addComponent(Component component, int min, int pref,
 1523                   int max) {
 1524               return addSpring(new ComponentSpring(component, min, pref, max));
 1525           }
 1526   
 1527           /**
 1528            * Adds a rigid gap to this {@code Group}.
 1529            *
 1530            * @param size the size of the gap
 1531            * @return this {@code Group}
 1532            * @throws IllegalArgumentException if {@code size} is less than
 1533            *         {@code 0}
 1534            */
 1535           public Group addGap(int size) {
 1536               return addGap(size, size, size);
 1537           }
 1538   
 1539           /**
 1540            * Adds a gap to this {@code Group} with the specified size.
 1541            *
 1542            * @param min the minimum size of the gap
 1543            * @param pref the preferred size of the gap
 1544            * @param max the maximum size of the gap
 1545            * @throws IllegalArgumentException if any of the values are
 1546            *         less than {@code 0}
 1547            * @return this {@code Group}
 1548            */
 1549           public Group addGap(int min, int pref, int max) {
 1550               return addSpring(new GapSpring(min, pref, max));
 1551           }
 1552   
 1553           Spring getSpring(int index) {
 1554               return springs.get(index);
 1555           }
 1556   
 1557           int indexOf(Spring spring) {
 1558               return springs.indexOf(spring);
 1559           }
 1560   
 1561           /**
 1562            * Adds the Spring to the list of {@code Spring}s and returns
 1563            * the receiver.
 1564            */
 1565           Group addSpring(Spring spring) {
 1566               springs.add(spring);
 1567               spring.setParent(this);
 1568               if (!(spring instanceof AutoPreferredGapSpring) ||
 1569                       !((AutoPreferredGapSpring)spring).getUserCreated()) {
 1570                   springsChanged = true;
 1571               }
 1572               return this;
 1573           }
 1574   
 1575           //
 1576           // Spring methods
 1577           //
 1578   
 1579           void setSize(int axis, int origin, int size) {
 1580               super.setSize(axis, origin, size);
 1581               if (size == UNSET) {
 1582                   for (int counter = springs.size() - 1; counter >= 0;
 1583                   counter--) {
 1584                       getSpring(counter).setSize(axis, origin, size);
 1585                   }
 1586               } else {
 1587                   setValidSize(axis, origin, size);
 1588               }
 1589           }
 1590   
 1591           /**
 1592            * This is invoked from {@code setSize} if passed a value
 1593            * other than UNSET.
 1594            */
 1595           abstract void setValidSize(int axis, int origin, int size);
 1596   
 1597           int calculateMinimumSize(int axis) {
 1598               return calculateSize(axis, MIN_SIZE);
 1599           }
 1600   
 1601           int calculatePreferredSize(int axis) {
 1602               return calculateSize(axis, PREF_SIZE);
 1603           }
 1604   
 1605           int calculateMaximumSize(int axis) {
 1606               return calculateSize(axis, MAX_SIZE);
 1607           }
 1608   
 1609           /**
 1610            * Calculates the specified size.  This is called from
 1611            * one of the {@code getMinimumSize0},
 1612            * {@code getPreferredSize0} or
 1613            * {@code getMaximumSize0} methods.  This will invoke
 1614            * to {@code operator} to combine the values.
 1615            */
 1616           int calculateSize(int axis, int type) {
 1617               int count = springs.size();
 1618               if (count == 0) {
 1619                   return 0;
 1620               }
 1621               if (count == 1) {
 1622                   return getSpringSize(getSpring(0), axis, type);
 1623               }
 1624               int size = constrain(operator(getSpringSize(getSpring(0), axis,
 1625                       type), getSpringSize(getSpring(1), axis, type)));
 1626               for (int counter = 2; counter < count; counter++) {
 1627                   size = constrain(operator(size, getSpringSize(
 1628                           getSpring(counter), axis, type)));
 1629               }
 1630               return size;
 1631           }
 1632   
 1633           int getSpringSize(Spring spring, int axis, int type) {
 1634               switch(type) {
 1635                   case MIN_SIZE:
 1636                       return spring.getMinimumSize(axis);
 1637                   case PREF_SIZE:
 1638                       return spring.getPreferredSize(axis);
 1639                   case MAX_SIZE:
 1640                       return spring.getMaximumSize(axis);
 1641               }
 1642               assert false;
 1643               return 0;
 1644           }
 1645   
 1646           /**
 1647            * Used to compute how the two values representing two springs
 1648            * will be combined.  For example, a group that layed things out
 1649            * one after the next would return {@code a + b}.
 1650            */
 1651           abstract int operator(int a, int b);
 1652   
 1653           //
 1654           // Padding
 1655           //
 1656   
 1657           /**
 1658            * Adjusts the autopadding springs in this group and its children.
 1659            * If {@code insert} is true this will insert auto padding
 1660            * springs, otherwise this will only adjust the springs that
 1661            * comprise auto preferred padding springs.
 1662            *
 1663            * @param axis the axis of the springs; HORIZONTAL or VERTICAL
 1664            * @param leadingPadding List of AutopaddingSprings that occur before
 1665            *                       this Group
 1666            * @param trailingPadding any trailing autopadding springs are added
 1667            *                        to this on exit
 1668            * @param leading List of ComponentSprings that occur before this Group
 1669            * @param trailing any trailing ComponentSpring are added to this
 1670            *                 List
 1671            * @param insert Whether or not to insert AutopaddingSprings or just
 1672            *               adjust any existing AutopaddingSprings.
 1673            */
 1674           abstract void insertAutopadding(int axis,
 1675                   List<AutoPreferredGapSpring> leadingPadding,
 1676                   List<AutoPreferredGapSpring> trailingPadding,
 1677                   List<ComponentSpring> leading, List<ComponentSpring> trailing,
 1678                   boolean insert);
 1679   
 1680           /**
 1681            * Removes any AutopaddingSprings for this Group and its children.
 1682            */
 1683           void removeAutopadding() {
 1684               unset();
 1685               for (int counter = springs.size() - 1; counter >= 0; counter--) {
 1686                   Spring spring = springs.get(counter);
 1687                   if (spring instanceof AutoPreferredGapSpring) {
 1688                       if (((AutoPreferredGapSpring)spring).getUserCreated()) {
 1689                           ((AutoPreferredGapSpring)spring).reset();
 1690                       } else {
 1691                           springs.remove(counter);
 1692                       }
 1693                   } else if (spring instanceof Group) {
 1694                       ((Group)spring).removeAutopadding();
 1695                   }
 1696               }
 1697           }
 1698   
 1699           void unsetAutopadding() {
 1700               // Clear cached pref/min/max.
 1701               unset();
 1702               for (int counter = springs.size() - 1; counter >= 0; counter--) {
 1703                   Spring spring = springs.get(counter);
 1704                   if (spring instanceof AutoPreferredGapSpring) {
 1705                       spring.unset();
 1706                   } else if (spring instanceof Group) {
 1707                       ((Group)spring).unsetAutopadding();
 1708                   }
 1709               }
 1710           }
 1711   
 1712           void calculateAutopadding(int axis) {
 1713               for (int counter = springs.size() - 1; counter >= 0; counter--) {
 1714                   Spring spring = springs.get(counter);
 1715                   if (spring instanceof AutoPreferredGapSpring) {
 1716                       // Force size to be reset.
 1717                       spring.unset();
 1718                       ((AutoPreferredGapSpring)spring).calculatePadding(axis);
 1719                   } else if (spring instanceof Group) {
 1720                       ((Group)spring).calculateAutopadding(axis);
 1721                   }
 1722               }
 1723               // Clear cached pref/min/max.
 1724               unset();
 1725           }
 1726   
 1727           @Override
 1728           boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
 1729               for (int i = springs.size() - 1; i >= 0; i--) {
 1730                   Spring spring = springs.get(i);
 1731                   if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {
 1732                       return false;
 1733                   }
 1734               }
 1735               return true;
 1736           }
 1737       }
 1738   
 1739   
 1740       /**
 1741        * A {@code Group} that positions and sizes its elements
 1742        * sequentially, one after another.  This class has no public
 1743        * constructor, use the {@code createSequentialGroup} method
 1744        * to create one.
 1745        * <p>
 1746        * In order to align a {@code SequentialGroup} along the baseline
 1747        * of a baseline aligned {@code ParallelGroup} you need to specify
 1748        * which of the elements of the {@code SequentialGroup} is used to
 1749        * determine the baseline.  The element used to calculate the
 1750        * baseline is specified using one of the {@code add} methods that
 1751        * take a {@code boolean}. The last element added with a value of
 1752        * {@code true} for {@code useAsBaseline} is used to calculate the
 1753        * baseline.
 1754        *
 1755        * @see #createSequentialGroup
 1756        * @since 1.6
 1757        */
 1758       public class SequentialGroup extends Group {
 1759           private Spring baselineSpring;
 1760   
 1761           SequentialGroup() {
 1762           }
 1763   
 1764           /**
 1765            * {@inheritDoc}
 1766            */
 1767           public SequentialGroup addGroup(Group group) {
 1768               return (SequentialGroup)super.addGroup(group);
 1769           }
 1770   
 1771           /**
 1772            * Adds a {@code Group} to this {@code Group}.
 1773            *
 1774            * @param group the {@code Group} to add
 1775            * @param useAsBaseline whether the specified {@code Group} should
 1776            *        be used to calculate the baseline for this {@code Group}
 1777            * @return this {@code Group}
 1778            */
 1779           public SequentialGroup addGroup(boolean useAsBaseline, Group group) {
 1780               super.addGroup(group);
 1781               if (useAsBaseline) {
 1782                   baselineSpring = group;
 1783               }
 1784               return this;
 1785           }
 1786   
 1787           /**
 1788            * {@inheritDoc}
 1789            */
 1790           public SequentialGroup addComponent(Component component) {
 1791               return (SequentialGroup)super.addComponent(component);
 1792           }
 1793   
 1794           /**
 1795            * Adds a {@code Component} to this {@code Group}.
 1796            *
 1797            * @param useAsBaseline whether the specified {@code Component} should
 1798            *        be used to calculate the baseline for this {@code Group}
 1799            * @param component the {@code Component} to add
 1800            * @return this {@code Group}
 1801            */
 1802           public SequentialGroup addComponent(boolean useAsBaseline,
 1803                   Component component) {
 1804               super.addComponent(component);
 1805               if (useAsBaseline) {
 1806                   baselineSpring = springs.get(springs.size() - 1);
 1807               }
 1808               return this;
 1809           }
 1810   
 1811           /**
 1812            * {@inheritDoc}
 1813            */
 1814           public SequentialGroup addComponent(Component component, int min,
 1815                   int pref, int max) {
 1816               return (SequentialGroup)super.addComponent(
 1817                       component, min, pref, max);
 1818           }
 1819   
 1820           /**
 1821            * Adds a {@code Component} to this {@code Group}
 1822            * with the specified size.
 1823            *
 1824            * @param useAsBaseline whether the specified {@code Component} should
 1825            *        be used to calculate the baseline for this {@code Group}
 1826            * @param component the {@code Component} to add
 1827            * @param min the minimum size or one of {@code DEFAULT_SIZE} or
 1828            *            {@code PREFERRED_SIZE}
 1829            * @param pref the preferred size or one of {@code DEFAULT_SIZE} or
 1830            *            {@code PREFERRED_SIZE}
 1831            * @param max the maximum size or one of {@code DEFAULT_SIZE} or
 1832            *            {@code PREFERRED_SIZE}
 1833            * @return this {@code Group}
 1834            */
 1835           public SequentialGroup addComponent(boolean useAsBaseline,
 1836                   Component component, int min, int pref, int max) {
 1837               super.addComponent(component, min, pref, max);
 1838               if (useAsBaseline) {
 1839                   baselineSpring = springs.get(springs.size() - 1);
 1840               }
 1841               return this;
 1842           }
 1843   
 1844           /**
 1845            * {@inheritDoc}
 1846            */
 1847           public SequentialGroup addGap(int size) {
 1848               return (SequentialGroup)super.addGap(size);
 1849           }
 1850   
 1851           /**
 1852            * {@inheritDoc}
 1853            */
 1854           public SequentialGroup addGap(int min, int pref, int max) {
 1855               return (SequentialGroup)super.addGap(min, pref, max);
 1856           }
 1857   
 1858           /**
 1859            * Adds an element representing the preferred gap between two
 1860            * components. The element created to represent the gap is not
 1861            * resizable.
 1862            *
 1863            * @param comp1 the first component
 1864            * @param comp2 the second component
 1865            * @param type the type of gap; one of the constants defined by
 1866            *        {@code LayoutStyle}
 1867            * @return this {@code SequentialGroup}
 1868            * @throws IllegalArgumentException if {@code type}, {@code comp1} or
 1869            *         {@code comp2} is {@code null}
 1870            * @see LayoutStyle
 1871            */
 1872           public SequentialGroup addPreferredGap(JComponent comp1,
 1873                   JComponent comp2, ComponentPlacement type) {
 1874               return addPreferredGap(comp1, comp2, type, DEFAULT_SIZE,
 1875                       PREFERRED_SIZE);
 1876           }
 1877   
 1878           /**
 1879            * Adds an element representing the preferred gap between two
 1880            * components.
 1881            *
 1882            * @param comp1 the first component
 1883            * @param comp2 the second component
 1884            * @param type the type of gap
 1885            * @param pref the preferred size of the grap; one of
 1886            *        {@code DEFAULT_SIZE} or a value &gt;= 0
 1887            * @param max the maximum size of the gap; one of
 1888            *        {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}
 1889            *        or a value &gt;= 0
 1890            * @return this {@code SequentialGroup}
 1891            * @throws IllegalArgumentException if {@code type}, {@code comp1} or
 1892            *         {@code comp2} is {@code null}
 1893            * @see LayoutStyle
 1894            */
 1895           public SequentialGroup addPreferredGap(JComponent comp1,
 1896                   JComponent comp2, ComponentPlacement type, int pref,
 1897                   int max) {
 1898               if (type == null) {
 1899                   throw new IllegalArgumentException("Type must be non-null");
 1900               }
 1901               if (comp1 == null || comp2 == null) {
 1902                   throw new IllegalArgumentException(
 1903                           "Components must be non-null");
 1904               }
 1905               checkPreferredGapValues(pref, max);
 1906               return (SequentialGroup)addSpring(new PreferredGapSpring(
 1907                       comp1, comp2, type, pref, max));
 1908           }
 1909   
 1910           /**
 1911            * Adds an element representing the preferred gap between the
 1912            * nearest components.  During layout, neighboring
 1913            * components are found, and the size of the added gap is set
 1914            * based on the preferred gap between the components.  If no
 1915            * neighboring components are found the gap has a size of {@code 0}.
 1916            * <p>
 1917            * The element created to represent the gap is not
 1918            * resizable.
 1919            *
 1920            * @param type the type of gap; one of
 1921            *        {@code LayoutStyle.ComponentPlacement.RELATED} or
 1922            *        {@code LayoutStyle.ComponentPlacement.UNRELATED}
 1923            * @return this {@code SequentialGroup}
 1924            * @see LayoutStyle
 1925            * @throws IllegalArgumentException if {@code type} is not one of
 1926            *         {@code LayoutStyle.ComponentPlacement.RELATED} or
 1927            *         {@code LayoutStyle.ComponentPlacement.UNRELATED}
 1928            */
 1929           public SequentialGroup addPreferredGap(ComponentPlacement type) {
 1930               return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);
 1931           }
 1932   
 1933           /**
 1934            * Adds an element representing the preferred gap between the
 1935            * nearest components.  During layout, neighboring
 1936            * components are found, and the minimum of this
 1937            * gap is set based on the size of the preferred gap between the
 1938            * neighboring components.  If no neighboring components are found the
 1939            * minimum size is set to 0.
 1940            *
 1941            * @param type the type of gap; one of
 1942            *        {@code LayoutStyle.ComponentPlacement.RELATED} or
 1943            *        {@code LayoutStyle.ComponentPlacement.UNRELATED}
 1944            * @param pref the preferred size of the grap; one of
 1945            *        {@code DEFAULT_SIZE} or a value &gt;= 0
 1946            * @param max the maximum size of the gap; one of
 1947            *        {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}
 1948            *        or a value &gt;= 0
 1949            * @return this {@code SequentialGroup}
 1950            * @throws IllegalArgumentException if {@code type} is not one of
 1951            *         {@code LayoutStyle.ComponentPlacement.RELATED} or
 1952            *         {@code LayoutStyle.ComponentPlacement.UNRELATED}
 1953            * @see LayoutStyle
 1954            */
 1955           public SequentialGroup addPreferredGap(ComponentPlacement type,
 1956                   int pref, int max) {
 1957               if (type != ComponentPlacement.RELATED &&
 1958                       type != ComponentPlacement.UNRELATED) {
 1959                   throw new IllegalArgumentException(
 1960                           "Type must be one of " +
 1961                           "LayoutStyle.ComponentPlacement.RELATED or " +
 1962                           "LayoutStyle.ComponentPlacement.UNRELATED");
 1963               }
 1964               checkPreferredGapValues(pref, max);
 1965               hasPreferredPaddingSprings = true;
 1966               return (SequentialGroup)addSpring(new AutoPreferredGapSpring(
 1967                       type, pref, max));
 1968           }
 1969   
 1970           /**
 1971            * Adds an element representing the preferred gap between an edge
 1972            * the container and components that touch the border of the
 1973            * container. This has no effect if the added gap does not
 1974            * touch an edge of the parent container.
 1975            * <p>
 1976            * The element created to represent the gap is not
 1977            * resizable.
 1978            *
 1979            * @return this {@code SequentialGroup}
 1980            */
 1981           public SequentialGroup addContainerGap() {
 1982               return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);
 1983           }
 1984   
 1985           /**
 1986            * Adds an element representing the preferred gap between one
 1987            * edge of the container and the next or previous {@code
 1988            * Component} with the specified size. This has no
 1989            * effect if the next or previous element is not a {@code
 1990            * Component} and does not touch one edge of the parent
 1991            * container.
 1992            *
 1993            * @param pref the preferred size; one of {@code DEFAULT_SIZE} or a
 1994            *              value &gt;= 0
 1995            * @param max the maximum size; one of {@code DEFAULT_SIZE},
 1996            *        {@code PREFERRED_SIZE} or a value &gt;= 0
 1997            * @return this {@code SequentialGroup}
 1998            */
 1999           public SequentialGroup addContainerGap(int pref, int max) {
 2000               if ((pref < 0 && pref != DEFAULT_SIZE) ||
 2001                       (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
 2002                       (pref >= 0 && max >= 0 && pref > max)) {
 2003                   throw new IllegalArgumentException(
 2004                           "Pref and max must be either DEFAULT_VALUE " +
 2005                           "or >= 0 and pref <= max");
 2006               }
 2007               hasPreferredPaddingSprings = true;
 2008               return (SequentialGroup)addSpring(
 2009                       new ContainerAutoPreferredGapSpring(pref, max));
 2010           }
 2011   
 2012           int operator(int a, int b) {
 2013               return constrain(a) + constrain(b);
 2014           }
 2015   
 2016           void setValidSize(int axis, int origin, int size) {
 2017               int pref = getPreferredSize(axis);
 2018               if (size == pref) {
 2019                   // Layout at preferred size
 2020                   for (Spring spring : springs) {
 2021                       int springPref = spring.getPreferredSize(axis);
 2022                       spring.setSize(axis, origin, springPref);
 2023                       origin += springPref;
 2024                   }
 2025               } else if (springs.size() == 1) {
 2026                   Spring spring = getSpring(0);
 2027                   spring.setSize(axis, origin, Math.min(
 2028                           Math.max(size, spring.getMinimumSize(axis)),
 2029                           spring.getMaximumSize(axis)));
 2030               } else if (springs.size() > 1) {
 2031                   // Adjust between min/pref
 2032                   setValidSizeNotPreferred(axis, origin, size);
 2033               }
 2034           }
 2035   
 2036           private void setValidSizeNotPreferred(int axis, int origin, int size) {
 2037               int delta = size - getPreferredSize(axis);
 2038               assert delta != 0;
 2039               boolean useMin = (delta < 0);
 2040               int springCount = springs.size();
 2041               if (useMin) {
 2042                   delta *= -1;
 2043               }
 2044   
 2045               // The following algorithm if used for resizing springs:
 2046               // 1. Calculate the resizability of each spring (pref - min or
 2047               //    max - pref) into a list.
 2048               // 2. Sort the list in ascending order
 2049               // 3. Iterate through each of the resizable Springs, attempting
 2050               //    to give them (pref - size) / resizeCount
 2051               // 4. For any Springs that can not accomodate that much space
 2052               //    add the remainder back to the amount to distribute and
 2053               //    recalculate how must space the remaining springs will get.
 2054               // 5. Set the size of the springs.
 2055   
 2056               // First pass, sort the resizable springs into the List resizable
 2057               List<SpringDelta> resizable = buildResizableList(axis, useMin);
 2058               int resizableCount = resizable.size();
 2059   
 2060               if (resizableCount > 0) {
 2061                   // How much we would like to give each Spring.
 2062                   int sDelta = delta / resizableCount;
 2063                   // Remaining space.
 2064                   int slop = delta - sDelta * resizableCount;
 2065                   int[] sizes = new int[springCount];
 2066                   int sign = useMin ? -1 : 1;
 2067                   // Second pass, accumulate the resulting deltas (relative to
 2068                   // preferred) into sizes.
 2069                   for (int counter = 0; counter < resizableCount; counter++) {
 2070                       SpringDelta springDelta = resizable.get(counter);
 2071                       if ((counter + 1) == resizableCount) {
 2072                           sDelta += slop;
 2073                       }
 2074                       springDelta.delta = Math.min(sDelta, springDelta.delta);
 2075                       delta -= springDelta.delta;
 2076                       if (springDelta.delta != sDelta && counter + 1 <
 2077                               resizableCount) {
 2078                           // Spring didn't take all the space, reset how much
 2079                           // each spring will get.
 2080                           sDelta = delta / (resizableCount - counter - 1);
 2081                           slop = delta - sDelta * (resizableCount - counter - 1);
 2082                       }
 2083                       sizes[springDelta.index] = sign * springDelta.delta;
 2084                   }
 2085   
 2086                   // And finally set the size of each spring
 2087                   for (int counter = 0; counter < springCount; counter++) {
 2088                       Spring spring = getSpring(counter);
 2089                       int sSize = spring.getPreferredSize(axis) + sizes[counter];
 2090                       spring.setSize(axis, origin, sSize);
 2091                       origin += sSize;
 2092                   }
 2093               } else {
 2094                   // Nothing resizable, use the min or max of each of the
 2095                   // springs.
 2096                   for (int counter = 0; counter < springCount; counter++) {
 2097                       Spring spring = getSpring(counter);
 2098                       int sSize;
 2099                       if (useMin) {
 2100                           sSize = spring.getMinimumSize(axis);
 2101                       } else {
 2102                           sSize = spring.getMaximumSize(axis);
 2103                       }
 2104                       spring.setSize(axis, origin, sSize);
 2105                       origin += sSize;
 2106                   }
 2107               }
 2108           }
 2109   
 2110           /**
 2111            * Returns the sorted list of SpringDelta's for the current set of
 2112            * Springs. The list is ordered based on the amount of flexibility of
 2113            * the springs.
 2114            */
 2115           private List<SpringDelta> buildResizableList(int axis,
 2116                   boolean useMin) {
 2117               // First pass, figure out what is resizable
 2118               int size = springs.size();
 2119               List<SpringDelta> sorted = new ArrayList<SpringDelta>(size);
 2120               for (int counter = 0; counter < size; counter++) {
 2121                   Spring spring = getSpring(counter);
 2122                   int sDelta;
 2123                   if (useMin) {
 2124                       sDelta = spring.getPreferredSize(axis) -
 2125                               spring.getMinimumSize(axis);
 2126                   } else {
 2127                       sDelta = spring.getMaximumSize(axis) -
 2128                               spring.getPreferredSize(axis);
 2129                   }
 2130                   if (sDelta > 0) {
 2131                       sorted.add(new SpringDelta(counter, sDelta));
 2132                   }
 2133               }
 2134               Collections.sort(sorted);
 2135               return sorted;
 2136           }
 2137   
 2138           private int indexOfNextNonZeroSpring(
 2139                   int index, boolean treatAutopaddingAsZeroSized) {
 2140               while (index < springs.size()) {
 2141                   Spring spring = springs.get(index);
 2142                   if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {
 2143                       return index;
 2144                   }
 2145                   index++;
 2146               }
 2147               return index;
 2148           }
 2149   
 2150           @Override
 2151           void insertAutopadding(int axis,
 2152                   List<AutoPreferredGapSpring> leadingPadding,
 2153                   List<AutoPreferredGapSpring> trailingPadding,
 2154                   List<ComponentSpring> leading, List<ComponentSpring> trailing,
 2155                   boolean insert) {
 2156               List<AutoPreferredGapSpring> newLeadingPadding =
 2157                       new ArrayList<AutoPreferredGapSpring>(leadingPadding);
 2158               List<AutoPreferredGapSpring> newTrailingPadding =
 2159                       new ArrayList<AutoPreferredGapSpring>(1);
 2160               List<ComponentSpring> newLeading =
 2161                       new ArrayList<ComponentSpring>(leading);
 2162               List<ComponentSpring> newTrailing = null;
 2163               int counter = 0;
 2164               // Warning, this must use springs.size, as it may change during the
 2165               // loop.
 2166               while (counter < springs.size()) {
 2167                   Spring spring = getSpring(counter);
 2168                   if (spring instanceof AutoPreferredGapSpring) {
 2169                       if (newLeadingPadding.size() == 0) {
 2170                           // Autopadding spring. Set the sources of the
 2171                           // autopadding spring based on newLeading.
 2172                           AutoPreferredGapSpring padding =
 2173                               (AutoPreferredGapSpring)spring;
 2174                           padding.setSources(newLeading);
 2175                           newLeading.clear();
 2176                           counter = indexOfNextNonZeroSpring(counter + 1, true);
 2177                           if (counter == springs.size()) {
 2178                               // Last spring in the list, add it to
 2179                               // trailingPadding.
 2180                               if (!(padding instanceof
 2181                                     ContainerAutoPreferredGapSpring)) {
 2182                                   trailingPadding.add(padding);
 2183                               }
 2184                           } else {
 2185                               newLeadingPadding.clear();
 2186                               newLeadingPadding.add(padding);
 2187                           }
 2188                       } else {
 2189                           counter = indexOfNextNonZeroSpring(counter + 1, true);
 2190                       }
 2191                   } else {
 2192                       // Not a padding spring
 2193                       if (newLeading.size() > 0 && insert) {
 2194                           // There's leading ComponentSprings, create an
 2195                           // autopadding spring.
 2196                           AutoPreferredGapSpring padding =
 2197                                   new AutoPreferredGapSpring();
 2198                           // Force the newly created spring to be considered
 2199                           // by NOT incrementing counter
 2200                           springs.add(counter, padding);
 2201                           continue;
 2202                       }
 2203                       if (spring instanceof ComponentSpring) {
 2204                           // Spring is a Component, make it the target of any
 2205                           // leading AutopaddingSpring.
 2206                           ComponentSpring cSpring = (ComponentSpring)spring;
 2207                           if (!cSpring.isVisible()) {
 2208                               counter++;
 2209                               continue;
 2210                           }
 2211                           for (AutoPreferredGapSpring gapSpring : newLeadingPadding) {
 2212                               gapSpring.addTarget(cSpring, axis);
 2213                           }
 2214                           newLeading.clear();
 2215                           newLeadingPadding.clear();
 2216                           counter = indexOfNextNonZeroSpring(counter + 1, false);
 2217                           if (counter == springs.size()) {
 2218                               // Last Spring, add it to trailing
 2219                               trailing.add(cSpring);
 2220                           } else {
 2221                               // Not that last Spring, add it to leading
 2222                               newLeading.add(cSpring);
 2223                           }
 2224                       } else if (spring instanceof Group) {
 2225                           // Forward call to child Group
 2226                           if (newTrailing == null) {
 2227                               newTrailing = new ArrayList<ComponentSpring>(1);
 2228                           } else {
 2229                               newTrailing.clear();
 2230                           }
 2231                           newTrailingPadding.clear();
 2232                           ((Group)spring).insertAutopadding(axis,
 2233                                   newLeadingPadding, newTrailingPadding,
 2234                                   newLeading, newTrailing, insert);
 2235                           newLeading.clear();
 2236                           newLeadingPadding.clear();
 2237                           counter = indexOfNextNonZeroSpring(
 2238                                       counter + 1, (newTrailing.size() == 0));
 2239                           if (counter == springs.size()) {
 2240                               trailing.addAll(newTrailing);
 2241                               trailingPadding.addAll(newTrailingPadding);
 2242                           } else {
 2243                               newLeading.addAll(newTrailing);
 2244                               newLeadingPadding.addAll(newTrailingPadding);
 2245                           }
 2246                       } else {
 2247                           // Gap
 2248                           newLeadingPadding.clear();
 2249                           newLeading.clear();
 2250                           counter++;
 2251                       }
 2252                   }
 2253               }
 2254           }
 2255   
 2256           int getBaseline() {
 2257               if (baselineSpring != null) {
 2258                   int baseline = baselineSpring.getBaseline();
 2259                   if (baseline >= 0) {
 2260                       int size = 0;
 2261                       for (Spring spring : springs) {
 2262                           if (spring == baselineSpring) {
 2263                               return size + baseline;
 2264                           } else {
 2265                               size += spring.getPreferredSize(VERTICAL);
 2266                           }
 2267                       }
 2268                   }
 2269               }
 2270               return -1;
 2271           }
 2272   
 2273           BaselineResizeBehavior getBaselineResizeBehavior() {
 2274               if (isResizable(VERTICAL)) {
 2275                   if (!baselineSpring.isResizable(VERTICAL)) {
 2276                       // Spring to use for baseline isn't resizable. In this case
 2277                       // baseline resize behavior can be determined based on how
 2278                       // preceeding springs resize.
 2279                       boolean leadingResizable = false;
 2280                       for (Spring spring : springs) {
 2281                           if (spring == baselineSpring) {
 2282                               break;
 2283                           } else if (spring.isResizable(VERTICAL)) {
 2284                               leadingResizable = true;
 2285                               break;
 2286                           }
 2287                       }
 2288                       boolean trailingResizable = false;
 2289                       for (int i = springs.size() - 1; i >= 0; i--) {
 2290                           Spring spring = springs.get(i);
 2291                           if (spring == baselineSpring) {
 2292                               break;
 2293                           }
 2294                           if (spring.isResizable(VERTICAL)) {
 2295                               trailingResizable = true;
 2296                               break;
 2297                           }
 2298                       }
 2299                       if (leadingResizable && !trailingResizable) {
 2300                           return BaselineResizeBehavior.CONSTANT_DESCENT;
 2301                       } else if (!leadingResizable && trailingResizable) {
 2302                           return BaselineResizeBehavior.CONSTANT_ASCENT;
 2303                       }
 2304                       // If we get here, both leading and trailing springs are
 2305                       // resizable. Fall through to OTHER.
 2306                   } else {
 2307                       BaselineResizeBehavior brb = baselineSpring.getBaselineResizeBehavior();
 2308                       if (brb == BaselineResizeBehavior.CONSTANT_ASCENT) {
 2309                           for (Spring spring : springs) {
 2310                               if (spring == baselineSpring) {
 2311                                   return BaselineResizeBehavior.CONSTANT_ASCENT;
 2312                               }
 2313                               if (spring.isResizable(VERTICAL)) {
 2314                                   return BaselineResizeBehavior.OTHER;
 2315                               }
 2316                           }
 2317                       } else if (brb == BaselineResizeBehavior.CONSTANT_DESCENT) {
 2318                           for (int i = springs.size() - 1; i >= 0; i--) {
 2319                               Spring spring = springs.get(i);
 2320                               if (spring == baselineSpring) {
 2321                                   return BaselineResizeBehavior.CONSTANT_DESCENT;
 2322                               }
 2323                               if (spring.isResizable(VERTICAL)) {
 2324                                   return BaselineResizeBehavior.OTHER;
 2325                               }
 2326                           }
 2327                       }
 2328                   }
 2329                   return BaselineResizeBehavior.OTHER;
 2330               }
 2331               // Not resizable, treat as constant_ascent
 2332               return BaselineResizeBehavior.CONSTANT_ASCENT;
 2333           }
 2334   
 2335           private void checkPreferredGapValues(int pref, int max) {
 2336               if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) ||
 2337                       (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
 2338                       (pref >= 0 && max >= 0 && pref > max)) {
 2339                   throw new IllegalArgumentException(
 2340                           "Pref and max must be either DEFAULT_SIZE, " +
 2341                           "PREFERRED_SIZE, or >= 0 and pref <= max");
 2342               }
 2343           }
 2344       }
 2345   
 2346   
 2347       /**
 2348        * Used by SequentialGroup in calculating resizability of springs.
 2349        */
 2350       private static final class SpringDelta implements Comparable<SpringDelta> {
 2351           // Original index.
 2352           public final int index;
 2353           // Delta, one of pref - min or max - pref.
 2354           public int delta;
 2355   
 2356           public SpringDelta(int index, int delta) {
 2357               this.index = index;
 2358               this.delta = delta;
 2359           }
 2360   
 2361           public int compareTo(SpringDelta o) {
 2362               return delta - o.delta;
 2363           }
 2364   
 2365           public String toString() {
 2366               return super.toString() + "[index=" + index + ", delta=" +
 2367                       delta + "]";
 2368           }
 2369       }
 2370   
 2371   
 2372       /**
 2373        * A {@code Group} that aligns and sizes it's children.
 2374        * {@code ParallelGroup} aligns it's children in
 2375        * four possible ways: along the baseline, centered, anchored to the
 2376        * leading edge, or anchored to the trailing edge.
 2377        * <h3>Baseline</h3>
 2378        * A {@code ParallelGroup} that aligns it's children along the
 2379        * baseline must first decide where the baseline is
 2380        * anchored. The baseline can either be anchored to the top, or
 2381        * anchored to the bottom of the group. That is, the distance between the
 2382        * baseline and the beginning of the group can be a constant
 2383        * distance, or the distance between the end of the group and the
 2384        * baseline can be a constant distance. The possible choices
 2385        * correspond to the {@code BaselineResizeBehavior} constants
 2386        * {@link
 2387        * java.awt.Component.BaselineResizeBehavior#CONSTANT_ASCENT CONSTANT_ASCENT} and
 2388        * {@link
 2389        * java.awt.Component.BaselineResizeBehavior#CONSTANT_DESCENT CONSTANT_DESCENT}.
 2390        * <p>
 2391        * The baseline anchor may be explicitly specified by the
 2392        * {@code createBaselineGroup} method, or determined based on the elements.
 2393        * If not explicitly specified, the baseline will be anchored to
 2394        * the bottom if all the elements with a baseline, and that are
 2395        * aligned to the baseline, have a baseline resize behavior of
 2396        * {@code CONSTANT_DESCENT}; otherwise the baseline is anchored to the top
 2397        * of the group.
 2398        * <p>
 2399        * Elements aligned to the baseline are resizable if they have have
 2400        * a baseline resize behavior of {@code CONSTANT_ASCENT} or
 2401        * {@code CONSTANT_DESCENT}. Elements with a baseline resize
 2402        * behavior of {@code OTHER} or {@code CENTER_OFFSET} are not resizable.
 2403        * <p>
 2404        * The baseline is calculated based on the preferred height of each
 2405        * of the elements that have a baseline. The baseline is
 2406        * calculated using the following algorithm:
 2407        * {@code max(maxNonBaselineHeight, maxAscent + maxDescent)}, where the
 2408        * {@code maxNonBaselineHeight} is the maximum height of all elements
 2409        * that do not have a baseline, or are not aligned along the baseline.
 2410        * {@code maxAscent} is the maximum ascent (baseline) of all elements that
 2411        * have a baseline and are aligned along the baseline.
 2412        * {@code maxDescent} is the maximum descent (preferred height - baseline)
 2413        * of all elements that have a baseline and are aligned along the baseline.
 2414        * <p>
 2415        * A {@code ParallelGroup} that aligns it's elements along the baseline
 2416        * is only useful along the vertical axis. If you create a
 2417        * baseline group and use it along the horizontal axis an
 2418        * {@code IllegalStateException} is thrown when you ask
 2419        * {@code GroupLayout} for the minimum, preferred or maximum size or
 2420        * attempt to layout the components.
 2421        * <p>
 2422        * Elements that are not aligned to the baseline and smaller than the size
 2423        * of the {@code ParallelGroup} are positioned in one of three
 2424        * ways: centered, anchored to the leading edge, or anchored to the
 2425        * trailing edge.
 2426        *
 2427        * <h3>Non-baseline {@code ParallelGroup}</h3>
 2428        * {@code ParallelGroup}s created with an alignment other than
 2429        * {@code BASELINE} align elements that are smaller than the size
 2430        * of the group in one of three ways: centered, anchored to the
 2431        * leading edge, or anchored to the trailing edge.
 2432        * <p>
 2433        * The leading edge is based on the axis and {@code
 2434        * ComponentOrientation}.  For the vertical axis the top edge is
 2435        * always the leading edge, and the bottom edge is always the
 2436        * trailing edge. When the {@code ComponentOrientation} is {@code
 2437        * LEFT_TO_RIGHT}, the leading edge is the left edge and the
 2438        * trailing edge the right edge. A {@code ComponentOrientation} of
 2439        * {@code RIGHT_TO_LEFT} flips the left and right edges. Child
 2440        * elements are aligned based on the specified alignment the
 2441        * element was added with. If you do not specify an alignment, the
 2442        * alignment specified for the {@code ParallelGroup} is used.
 2443        * <p>
 2444        * To align elements along the baseline you {@code createBaselineGroup},
 2445        * or {@code createParallelGroup} with an alignment of {@code BASELINE}.
 2446        * If the group was not created with a baseline alignment, and you attempt
 2447        * to add an element specifying a baseline alignment, an
 2448        * {@code IllegalArgumentException} is thrown.
 2449        *
 2450        * @see #createParallelGroup()
 2451        * @see #createBaselineGroup(boolean,boolean)
 2452        * @since 1.6
 2453        */
 2454       public class ParallelGroup extends Group {
 2455           // How children are layed out.
 2456           private final Alignment childAlignment;
 2457           // Whether or not we're resizable.
 2458           private final boolean resizable;
 2459   
 2460           ParallelGroup(Alignment childAlignment, boolean resizable) {
 2461               this.childAlignment = childAlignment;
 2462               this.resizable = resizable;
 2463           }
 2464   
 2465           /**
 2466            * {@inheritDoc}
 2467            */
 2468           public ParallelGroup addGroup(Group group) {
 2469               return (ParallelGroup)super.addGroup(group);
 2470           }
 2471   
 2472           /**
 2473            * {@inheritDoc}
 2474            */
 2475           public ParallelGroup addComponent(Component component) {
 2476               return (ParallelGroup)super.addComponent(component);
 2477           }
 2478   
 2479           /**
 2480            * {@inheritDoc}
 2481            */
 2482           public ParallelGroup addComponent(Component component, int min, int pref,
 2483                   int max) {
 2484               return (ParallelGroup)super.addComponent(component, min, pref, max);
 2485           }
 2486   
 2487           /**
 2488            * {@inheritDoc}
 2489            */
 2490           public ParallelGroup addGap(int pref) {
 2491               return (ParallelGroup)super.addGap(pref);
 2492           }
 2493   
 2494           /**
 2495            * {@inheritDoc}
 2496            */
 2497           public ParallelGroup addGap(int min, int pref, int max) {
 2498               return (ParallelGroup)super.addGap(min, pref, max);
 2499           }
 2500   
 2501           /**
 2502            * Adds a {@code Group} to this {@code ParallelGroup} with the
 2503            * specified alignment. If the child is smaller than the
 2504            * {@code Group} it is aligned based on the specified
 2505            * alignment.
 2506            *
 2507            * @param alignment the alignment
 2508            * @param group the {@code Group} to add
 2509            * @return this {@code ParallelGroup}
 2510            * @throws IllegalArgumentException if {@code alignment} is
 2511            *         {@code null}
 2512            */
 2513           public ParallelGroup addGroup(Alignment alignment, Group group) {
 2514               checkChildAlignment(alignment);
 2515               group.setAlignment(alignment);
 2516               return (ParallelGroup)addSpring(group);
 2517           }
 2518   
 2519           /**
 2520            * Adds a {@code Component} to this {@code ParallelGroup} with
 2521            * the specified alignment.
 2522            *
 2523            * @param alignment the alignment
 2524            * @param component the {@code Component} to add
 2525            * @return this {@code Group}
 2526            * @throws IllegalArgumentException if {@code alignment} is
 2527            *         {@code null}
 2528            */
 2529           public ParallelGroup addComponent(Component component,
 2530                   Alignment alignment) {
 2531               return addComponent(component, alignment, DEFAULT_SIZE, DEFAULT_SIZE,
 2532                       DEFAULT_SIZE);
 2533           }
 2534   
 2535           /**
 2536            * Adds a {@code Component} to this {@code ParallelGroup} with the
 2537            * specified alignment and size.
 2538            *
 2539            * @param alignment the alignment
 2540            * @param component the {@code Component} to add
 2541            * @param min the minimum size
 2542            * @param pref the preferred size
 2543            * @param max the maximum size
 2544            * @throws IllegalArgumentException if {@code alignment} is
 2545            *         {@code null}
 2546            * @return this {@code Group}
 2547            */
 2548           public ParallelGroup addComponent(Component component,
 2549                   Alignment alignment, int min, int pref, int max) {
 2550               checkChildAlignment(alignment);
 2551               ComponentSpring spring = new ComponentSpring(component,
 2552                       min, pref, max);
 2553               spring.setAlignment(alignment);
 2554               return (ParallelGroup)addSpring(spring);
 2555           }
 2556   
 2557           boolean isResizable() {
 2558               return resizable;
 2559           }
 2560   
 2561           int operator(int a, int b) {
 2562               return Math.max(a, b);
 2563           }
 2564   
 2565           int calculateMinimumSize(int axis) {
 2566               if (!isResizable()) {
 2567                   return getPreferredSize(axis);
 2568               }
 2569               return super.calculateMinimumSize(axis);
 2570           }
 2571   
 2572           int calculateMaximumSize(int axis) {
 2573               if (!isResizable()) {
 2574                   return getPreferredSize(axis);
 2575               }
 2576               return super.calculateMaximumSize(axis);
 2577           }
 2578   
 2579           void setValidSize(int axis, int origin, int size) {
 2580               for (Spring spring : springs) {
 2581                   setChildSize(spring, axis, origin, size);
 2582               }
 2583           }
 2584   
 2585           void setChildSize(Spring spring, int axis, int origin, int size) {
 2586               Alignment alignment = spring.getAlignment();
 2587               int springSize = Math.min(
 2588                       Math.max(spring.getMinimumSize(axis), size),
 2589                       spring.getMaximumSize(axis));
 2590               if (alignment == null) {
 2591                   alignment = childAlignment;
 2592               }
 2593               switch (alignment) {
 2594                   case TRAILING:
 2595                       spring.setSize(axis, origin + size - springSize,
 2596                               springSize);
 2597                       break;
 2598                   case CENTER:
 2599                       spring.setSize(axis, origin +
 2600                               (size - springSize) / 2,springSize);
 2601                       break;
 2602                   default: // LEADING, or BASELINE
 2603                       spring.setSize(axis, origin, springSize);
 2604                       break;
 2605               }
 2606           }
 2607   
 2608           @Override
 2609           void insertAutopadding(int axis,
 2610                   List<AutoPreferredGapSpring> leadingPadding,
 2611                   List<AutoPreferredGapSpring> trailingPadding,
 2612                   List<ComponentSpring> leading, List<ComponentSpring> trailing,
 2613                   boolean insert) {
 2614               for (Spring spring : springs) {
 2615                   if (spring instanceof ComponentSpring) {
 2616                       if (((ComponentSpring)spring).isVisible()) {
 2617                           for (AutoPreferredGapSpring gapSpring :
 2618                                    leadingPadding) {
 2619                               gapSpring.addTarget((ComponentSpring)spring, axis);
 2620                           }
 2621                           trailing.add((ComponentSpring)spring);
 2622                       }
 2623                   } else if (spring instanceof Group) {
 2624                       ((Group)spring).insertAutopadding(axis, leadingPadding,
 2625                               trailingPadding, leading, trailing, insert);
 2626                   } else if (spring instanceof AutoPreferredGapSpring) {
 2627                       ((AutoPreferredGapSpring)spring).setSources(leading);
 2628                       trailingPadding.add((AutoPreferredGapSpring)spring);
 2629                   }
 2630               }
 2631           }
 2632   
 2633           private void checkChildAlignment(Alignment alignment) {
 2634               checkChildAlignment(alignment, (this instanceof BaselineGroup));
 2635           }
 2636   
 2637           private void checkChildAlignment(Alignment alignment,
 2638                   boolean allowsBaseline) {
 2639               if (alignment == null) {
 2640                   throw new IllegalArgumentException("Alignment must be non-null");
 2641               }
 2642               if (!allowsBaseline && alignment == Alignment.BASELINE) {
 2643                   throw new IllegalArgumentException("Alignment must be one of:" +
 2644                           "LEADING, TRAILING or CENTER");
 2645               }
 2646           }
 2647       }
 2648   
 2649   
 2650       /**
 2651        * An extension of {@code ParallelGroup} that aligns its
 2652        * constituent {@code Spring}s along the baseline.
 2653        */
 2654       private class BaselineGroup extends ParallelGroup {
 2655           // Whether or not all child springs have a baseline
 2656           private boolean allSpringsHaveBaseline;
 2657   
 2658           // max(spring.getBaseline()) of all springs aligned along the baseline
 2659           // that have a baseline
 2660           private int prefAscent;
 2661   
 2662           // max(spring.getPreferredSize().height - spring.getBaseline()) of all
 2663           // springs aligned along the baseline that have a baseline
 2664           private int prefDescent;
 2665   
 2666           // Whether baselineAnchoredToTop was explicitly set
 2667           private boolean baselineAnchorSet;
 2668   
 2669           // Whether the baseline is anchored to the top or the bottom.
 2670           // If anchored to the top the baseline is always at prefAscent,
 2671           // otherwise the baseline is at (height - prefDescent)
 2672           private boolean baselineAnchoredToTop;
 2673   
 2674           // Whether or not the baseline has been calculated.
 2675           private boolean calcedBaseline;
 2676   
 2677           BaselineGroup(boolean resizable) {
 2678               super(Alignment.LEADING, resizable);
 2679               prefAscent = prefDescent = -1;
 2680               calcedBaseline = false;
 2681           }
 2682   
 2683           BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) {
 2684               this(resizable);
 2685               this.baselineAnchoredToTop = baselineAnchoredToTop;
 2686               baselineAnchorSet = true;
 2687           }
 2688   
 2689           void unset() {
 2690               super.unset();
 2691               prefAscent = prefDescent = -1;
 2692               calcedBaseline = false;
 2693           }
 2694   
 2695           void setValidSize(int axis, int origin, int size) {
 2696               checkAxis(axis);
 2697               if (prefAscent == -1) {
 2698                   super.setValidSize(axis, origin, size);
 2699               } else {
 2700                   // do baseline layout
 2701                   baselineLayout(origin, size);
 2702               }
 2703           }
 2704   
 2705           int calculateSize(int axis, int type) {
 2706               checkAxis(axis);
 2707               if (!calcedBaseline) {
 2708                   calculateBaselineAndResizeBehavior();
 2709               }
 2710               if (type == MIN_SIZE) {
 2711                   return calculateMinSize();
 2712               }
 2713               if (type == MAX_SIZE) {
 2714                   return calculateMaxSize();
 2715               }
 2716               if (allSpringsHaveBaseline) {
 2717                   return prefAscent + prefDescent;
 2718               }
 2719               return Math.max(prefAscent + prefDescent,
 2720                       super.calculateSize(axis, type));
 2721           }
 2722   
 2723           private void calculateBaselineAndResizeBehavior() {
 2724               // calculate baseline
 2725               prefAscent = 0;
 2726               prefDescent = 0;
 2727               int baselineSpringCount = 0;
 2728               BaselineResizeBehavior resizeBehavior = null;
 2729               for (Spring spring : springs) {
 2730                   if (spring.getAlignment() == null ||
 2731                           spring.getAlignment() == Alignment.BASELINE) {
 2732                       int baseline = spring.getBaseline();
 2733                       if (baseline >= 0) {
 2734                           if (spring.isResizable(VERTICAL)) {
 2735                               BaselineResizeBehavior brb = spring.
 2736                                       getBaselineResizeBehavior();
 2737                               if (resizeBehavior == null) {
 2738                                   resizeBehavior = brb;
 2739                               } else if (brb != resizeBehavior) {
 2740                                   resizeBehavior = BaselineResizeBehavior.
 2741                                           CONSTANT_ASCENT;
 2742                               }
 2743                           }
 2744                           prefAscent = Math.max(prefAscent, baseline);
 2745                           prefDescent = Math.max(prefDescent, spring.
 2746                                   getPreferredSize(VERTICAL) - baseline);
 2747                           baselineSpringCount++;
 2748                       }
 2749                   }
 2750               }
 2751               if (!baselineAnchorSet) {
 2752                   if (resizeBehavior == BaselineResizeBehavior.CONSTANT_DESCENT){
 2753                       this.baselineAnchoredToTop = false;
 2754                   } else {
 2755                       this.baselineAnchoredToTop = true;
 2756                   }
 2757               }
 2758               allSpringsHaveBaseline = (baselineSpringCount == springs.size());
 2759               calcedBaseline = true;
 2760           }
 2761   
 2762           private int calculateMaxSize() {
 2763               int maxAscent = prefAscent;
 2764               int maxDescent = prefDescent;
 2765               int nonBaselineMax = 0;
 2766               for (Spring spring : springs) {
 2767                   int baseline;
 2768                   int springMax = spring.getMaximumSize(VERTICAL);
 2769                   if ((spring.getAlignment() == null ||
 2770                           spring.getAlignment() == Alignment.BASELINE) &&
 2771                           (baseline = spring.getBaseline()) >= 0) {
 2772                       int springPref = spring.getPreferredSize(VERTICAL);
 2773                       if (springPref != springMax) {
 2774                           switch (spring.getBaselineResizeBehavior()) {
 2775                               case CONSTANT_ASCENT:
 2776                                   if (baselineAnchoredToTop) {
 2777                                       maxDescent = Math.max(maxDescent,
 2778                                               springMax - baseline);
 2779                                   }
 2780                                   break;
 2781                               case CONSTANT_DESCENT:
 2782                                   if (!baselineAnchoredToTop) {
 2783                                       maxAscent = Math.max(maxAscent,
 2784                                               springMax - springPref + baseline);
 2785                                   }
 2786                                   break;
 2787                               default: // CENTER_OFFSET and OTHER, not resizable
 2788                                   break;
 2789                           }
 2790                       }
 2791                   } else {
 2792                       // Not aligned along the baseline, or no baseline.
 2793                       nonBaselineMax = Math.max(nonBaselineMax, springMax);
 2794                   }
 2795               }
 2796               return Math.max(nonBaselineMax, maxAscent + maxDescent);
 2797           }
 2798   
 2799           private int calculateMinSize() {
 2800               int minAscent = 0;
 2801               int minDescent = 0;
 2802               int nonBaselineMin = 0;
 2803               if (baselineAnchoredToTop) {
 2804                   minAscent = prefAscent;
 2805               } else {
 2806                   minDescent = prefDescent;
 2807               }
 2808               for (Spring spring : springs) {
 2809                   int springMin = spring.getMinimumSize(VERTICAL);
 2810                   int baseline;
 2811                   if ((spring.getAlignment() == null ||
 2812                           spring.getAlignment() == Alignment.BASELINE) &&
 2813                           (baseline = spring.getBaseline()) >= 0) {
 2814                       int springPref = spring.getPreferredSize(VERTICAL);
 2815                       BaselineResizeBehavior brb = spring.
 2816                               getBaselineResizeBehavior();
 2817                       switch (brb) {
 2818                           case CONSTANT_ASCENT:
 2819                               if (baselineAnchoredToTop) {
 2820                                   minDescent = Math.max(springMin - baseline,
 2821                                           minDescent);
 2822                               } else {
 2823                                   minAscent = Math.max(baseline, minAscent);
 2824                               }
 2825                               break;
 2826                           case CONSTANT_DESCENT:
 2827                               if (!baselineAnchoredToTop) {
 2828                                   minAscent = Math.max(
 2829                                           baseline - (springPref - springMin),
 2830                                           minAscent);
 2831                               } else {
 2832                                   minDescent = Math.max(springPref - baseline,
 2833                                           minDescent);
 2834                               }
 2835                               break;
 2836                           default:
 2837                               // CENTER_OFFSET and OTHER are !resizable, use
 2838                               // the preferred size.
 2839                               minAscent = Math.max(baseline, minAscent);
 2840                               minDescent = Math.max(springPref - baseline,
 2841                                       minDescent);
 2842                               break;
 2843                       }
 2844                   } else {
 2845                       // Not aligned along the baseline, or no baseline.
 2846                       nonBaselineMin = Math.max(nonBaselineMin, springMin);
 2847                   }
 2848               }
 2849               return Math.max(nonBaselineMin, minAscent + minDescent);
 2850           }
 2851   
 2852           /**
 2853            * Lays out springs that have a baseline along the baseline.  All
 2854            * others are centered.
 2855            */
 2856           private void baselineLayout(int origin, int size) {
 2857               int ascent;
 2858               int descent;
 2859               if (baselineAnchoredToTop) {
 2860                   ascent = prefAscent;
 2861                   descent = size - ascent;
 2862               } else {
 2863                   ascent = size - prefDescent;
 2864                   descent = prefDescent;
 2865               }
 2866               for (Spring spring : springs) {
 2867                   Alignment alignment = spring.getAlignment();
 2868                   if (alignment == null || alignment == Alignment.BASELINE) {
 2869                       int baseline = spring.getBaseline();
 2870                       if (baseline >= 0) {
 2871                           int springMax = spring.getMaximumSize(VERTICAL);
 2872                           int springPref = spring.getPreferredSize(VERTICAL);
 2873                           int height = springPref;
 2874                           int y;
 2875                           switch(spring.getBaselineResizeBehavior()) {
 2876                               case CONSTANT_ASCENT:
 2877                                   y = origin + ascent - baseline;
 2878                                   height = Math.min(descent, springMax -
 2879                                           baseline) + baseline;
 2880                                   break;
 2881                               case CONSTANT_DESCENT:
 2882                                   height = Math.min(ascent, springMax -
 2883                                           springPref + baseline) +
 2884                                           (springPref - baseline);
 2885                                   y = origin + ascent +
 2886                                           (springPref - baseline) - height;
 2887                                   break;
 2888                               default: // CENTER_OFFSET & OTHER, not resizable
 2889                                   y = origin + ascent - baseline;
 2890                                   break;
 2891                           }
 2892                           spring.setSize(VERTICAL, y, height);
 2893                       } else {
 2894                           setChildSize(spring, VERTICAL, origin, size);
 2895                       }
 2896                   } else {
 2897                       setChildSize(spring, VERTICAL, origin, size);
 2898                   }
 2899               }
 2900           }
 2901   
 2902           int getBaseline() {
 2903               if (springs.size() > 1) {
 2904                   // Force the baseline to be calculated
 2905                   getPreferredSize(VERTICAL);
 2906                   return prefAscent;
 2907               } else if (springs.size() == 1) {
 2908                   return springs.get(0).getBaseline();
 2909               }
 2910               return -1;
 2911           }
 2912   
 2913           BaselineResizeBehavior getBaselineResizeBehavior() {
 2914               if (springs.size() == 1) {
 2915                   return springs.get(0).getBaselineResizeBehavior();
 2916               }
 2917               if (baselineAnchoredToTop) {
 2918                   return BaselineResizeBehavior.CONSTANT_ASCENT;
 2919               }
 2920               return BaselineResizeBehavior.CONSTANT_DESCENT;
 2921           }
 2922   
 2923           // If the axis is VERTICAL, throws an IllegalStateException
 2924           private void checkAxis(int axis) {
 2925               if (axis == HORIZONTAL) {
 2926                   throw new IllegalStateException(
 2927                           "Baseline must be used along vertical axis");
 2928               }
 2929           }
 2930       }
 2931   
 2932   
 2933       private final class ComponentSpring extends Spring {
 2934           private Component component;
 2935           private int origin;
 2936   
 2937           // min/pref/max are either a value >= 0 or one of
 2938           // DEFAULT_SIZE or PREFERRED_SIZE
 2939           private final int min;
 2940           private final int pref;
 2941           private final int max;
 2942   
 2943           // Baseline for the component, computed as necessary.
 2944           private int baseline = -1;
 2945   
 2946           // Whether or not the size has been requested yet.
 2947           private boolean installed;
 2948   
 2949           private ComponentSpring(Component component, int min, int pref,
 2950                   int max) {
 2951               this.component = component;
 2952               if (component == null) {
 2953                   throw new IllegalArgumentException(
 2954                           "Component must be non-null");
 2955               }
 2956   
 2957               checkSize(min, pref, max, true);
 2958   
 2959               this.min = min;
 2960               this.max = max;
 2961               this.pref = pref;
 2962   
 2963               // getComponentInfo makes sure component is a child of the
 2964               // Container GroupLayout is the LayoutManager for.
 2965               getComponentInfo(component);
 2966           }
 2967   
 2968           int calculateMinimumSize(int axis) {
 2969               if (isLinked(axis)) {
 2970                   return getLinkSize(axis, MIN_SIZE);
 2971               }
 2972               return calculateNonlinkedMinimumSize(axis);
 2973           }
 2974   
 2975           int calculatePreferredSize(int axis) {
 2976               if (isLinked(axis)) {
 2977                   return getLinkSize(axis, PREF_SIZE);
 2978               }
 2979               int min = getMinimumSize(axis);
 2980               int pref = calculateNonlinkedPreferredSize(axis);
 2981               int max = getMaximumSize(axis);
 2982               return Math.min(max, Math.max(min, pref));
 2983           }
 2984   
 2985           int calculateMaximumSize(int axis) {
 2986               if (isLinked(axis)) {
 2987                   return getLinkSize(axis, MAX_SIZE);
 2988               }
 2989               return Math.max(getMinimumSize(axis),
 2990                       calculateNonlinkedMaximumSize(axis));
 2991           }
 2992   
 2993           boolean isVisible() {
 2994               return getComponentInfo(getComponent()).isVisible();
 2995           }
 2996   
 2997           int calculateNonlinkedMinimumSize(int axis) {
 2998               if (!isVisible()) {
 2999                   return 0;
 3000               }
 3001               if (min >= 0) {
 3002                   return min;
 3003               }
 3004               if (min == PREFERRED_SIZE) {
 3005                   return calculateNonlinkedPreferredSize(axis);
 3006               }
 3007               assert (min == DEFAULT_SIZE);
 3008               return getSizeAlongAxis(axis, component.getMinimumSize());
 3009           }
 3010   
 3011           int calculateNonlinkedPreferredSize(int axis) {
 3012               if (!isVisible()) {
 3013                   return 0;
 3014               }
 3015               if (pref >= 0) {
 3016                   return pref;
 3017               }
 3018               assert (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE);
 3019               return getSizeAlongAxis(axis, component.getPreferredSize());
 3020           }
 3021   
 3022           int calculateNonlinkedMaximumSize(int axis) {
 3023               if (!isVisible()) {
 3024                   return 0;
 3025               }
 3026               if (max >= 0) {
 3027                   return max;
 3028               }
 3029               if (max == PREFERRED_SIZE) {
 3030                   return calculateNonlinkedPreferredSize(axis);
 3031               }
 3032               assert (max == DEFAULT_SIZE);
 3033               return getSizeAlongAxis(axis, component.getMaximumSize());
 3034           }
 3035   
 3036           private int getSizeAlongAxis(int axis, Dimension size) {
 3037               return (axis == HORIZONTAL) ? size.width : size.height;
 3038           }
 3039   
 3040           private int getLinkSize(int axis, int type) {
 3041               if (!isVisible()) {
 3042                   return 0;
 3043               }
 3044               ComponentInfo ci = getComponentInfo(component);
 3045               return ci.getLinkSize(axis, type);
 3046           }
 3047   
 3048           void setSize(int axis, int origin, int size) {
 3049               super.setSize(axis, origin, size);
 3050               this.origin = origin;
 3051               if (size == UNSET) {
 3052                   baseline = -1;
 3053               }
 3054           }
 3055   
 3056           int getOrigin() {
 3057               return origin;
 3058           }
 3059   
 3060           void setComponent(Component component) {
 3061               this.component = component;
 3062           }
 3063   
 3064           Component getComponent() {
 3065               return component;
 3066           }
 3067   
 3068           int getBaseline() {
 3069               if (baseline == -1) {
 3070                   Spring horizontalSpring = getComponentInfo(component).
 3071                           horizontalSpring;
 3072                   int width = horizontalSpring.getPreferredSize(HORIZONTAL);
 3073                   int height = getPreferredSize(VERTICAL);
 3074                   if (width > 0 && height > 0) {
 3075                       baseline = component.getBaseline(width, height);
 3076                   }
 3077               }
 3078               return baseline;
 3079           }
 3080   
 3081           BaselineResizeBehavior getBaselineResizeBehavior() {
 3082               return getComponent().getBaselineResizeBehavior();
 3083           }
 3084   
 3085           private boolean isLinked(int axis) {
 3086               return getComponentInfo(component).isLinked(axis);
 3087           }
 3088   
 3089           void installIfNecessary(int axis) {
 3090               if (!installed) {
 3091                   installed = true;
 3092                   if (axis == HORIZONTAL) {
 3093                       getComponentInfo(component).horizontalSpring = this;
 3094                   } else {
 3095                       getComponentInfo(component).verticalSpring = this;
 3096                   }
 3097               }
 3098           }
 3099   
 3100           @Override
 3101           boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
 3102               return !isVisible();
 3103           }
 3104       }
 3105   
 3106   
 3107       /**
 3108        * Spring representing the preferred distance between two components.
 3109        */
 3110       private class PreferredGapSpring extends Spring {
 3111           private final JComponent source;
 3112           private final JComponent target;
 3113           private final ComponentPlacement type;
 3114           private final int pref;
 3115           private final int max;
 3116   
 3117           PreferredGapSpring(JComponent source, JComponent target,
 3118                   ComponentPlacement type, int pref, int max) {
 3119               this.source = source;
 3120               this.target = target;
 3121               this.type = type;
 3122               this.pref = pref;
 3123               this.max = max;
 3124           }
 3125   
 3126           int calculateMinimumSize(int axis) {
 3127               return getPadding(axis);
 3128           }
 3129   
 3130           int calculatePreferredSize(int axis) {
 3131               if (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE) {
 3132                   return getMinimumSize(axis);
 3133               }
 3134               int min = getMinimumSize(axis);
 3135               int max = getMaximumSize(axis);
 3136               return Math.min(max, Math.max(min, pref));
 3137           }
 3138   
 3139           int calculateMaximumSize(int axis) {
 3140               if (max == PREFERRED_SIZE || max == DEFAULT_SIZE) {
 3141                   return getPadding(axis);
 3142               }
 3143               return Math.max(getMinimumSize(axis), max);
 3144           }
 3145   
 3146           private int getPadding(int axis) {
 3147               int position;
 3148               if (axis == HORIZONTAL) {
 3149                   position = SwingConstants.EAST;
 3150               } else {
 3151                   position = SwingConstants.SOUTH;
 3152               }
 3153               return getLayoutStyle0().getPreferredGap(source,
 3154                       target, type, position, host);
 3155           }
 3156   
 3157           @Override
 3158           boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
 3159               return false;
 3160           }
 3161       }
 3162   
 3163   
 3164       /**
 3165        * Spring represented a certain amount of space.
 3166        */
 3167       private class GapSpring extends Spring {
 3168           private final int min;
 3169           private final int pref;
 3170           private final int max;
 3171   
 3172           GapSpring(int min, int pref, int max) {
 3173               checkSize(min, pref, max, false);
 3174               this.min = min;
 3175               this.pref = pref;
 3176               this.max = max;
 3177           }
 3178   
 3179           int calculateMinimumSize(int axis) {
 3180               if (min == PREFERRED_SIZE) {
 3181                   return getPreferredSize(axis);
 3182               }
 3183               return min;
 3184           }
 3185   
 3186           int calculatePreferredSize(int axis) {
 3187               return pref;
 3188           }
 3189   
 3190           int calculateMaximumSize(int axis) {
 3191               if (max == PREFERRED_SIZE) {
 3192                   return getPreferredSize(axis);
 3193               }
 3194               return max;
 3195           }
 3196   
 3197           @Override
 3198           boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
 3199               return false;
 3200           }
 3201       }
 3202   
 3203   
 3204       /**
 3205        * Spring reprensenting the distance between any number of sources and
 3206        * targets.  The targets and sources are computed during layout.  An
 3207        * instance of this can either be dynamically created when
 3208        * autocreatePadding is true, or explicitly created by the developer.
 3209        */
 3210       private class AutoPreferredGapSpring extends Spring {
 3211           List<ComponentSpring> sources;
 3212           ComponentSpring source;
 3213           private List<AutoPreferredGapMatch> matches;
 3214           int size;
 3215           int lastSize;
 3216           private final int pref;
 3217           private final int max;
 3218           // Type of gap
 3219           private ComponentPlacement type;
 3220           private boolean userCreated;
 3221   
 3222           private AutoPreferredGapSpring() {
 3223               this.pref = PREFERRED_SIZE;
 3224               this.max = PREFERRED_SIZE;
 3225               this.type = ComponentPlacement.RELATED;
 3226           }
 3227   
 3228           AutoPreferredGapSpring(int pref, int max) {
 3229               this.pref = pref;
 3230               this.max = max;
 3231           }
 3232   
 3233           AutoPreferredGapSpring(ComponentPlacement type, int pref, int max) {
 3234               this.type = type;
 3235               this.pref = pref;
 3236               this.max = max;
 3237               this.userCreated = true;
 3238           }
 3239   
 3240           public void setSource(ComponentSpring source) {
 3241               this.source = source;
 3242           }
 3243   
 3244           public void setSources(List<ComponentSpring> sources) {
 3245               this.sources = new ArrayList<ComponentSpring>(sources);
 3246           }
 3247   
 3248           public void setUserCreated(boolean userCreated) {
 3249               this.userCreated = userCreated;
 3250           }
 3251   
 3252           public boolean getUserCreated() {
 3253               return userCreated;
 3254           }
 3255   
 3256           void unset() {
 3257               lastSize = getSize();
 3258               super.unset();
 3259               size = 0;
 3260           }
 3261   
 3262           public void reset() {
 3263               size = 0;
 3264               sources = null;
 3265               source = null;
 3266               matches = null;
 3267           }
 3268   
 3269           public void calculatePadding(int axis) {
 3270               size = UNSET;
 3271               int maxPadding = UNSET;
 3272               if (matches != null) {
 3273                   LayoutStyle p = getLayoutStyle0();
 3274                   int position;
 3275                   if (axis == HORIZONTAL) {
 3276                       if (isLeftToRight()) {
 3277                           position = SwingConstants.EAST;
 3278                       } else {
 3279                           position = SwingConstants.WEST;
 3280                       }
 3281                   } else {
 3282                       position = SwingConstants.SOUTH;
 3283                   }
 3284                   for (int i = matches.size() - 1; i >= 0; i--) {
 3285                       AutoPreferredGapMatch match = matches.get(i);
 3286                       maxPadding = Math.max(maxPadding,
 3287                               calculatePadding(p, position, match.source,
 3288                               match.target));
 3289                   }
 3290               }
 3291               if (size == UNSET) {
 3292                   size = 0;
 3293               }
 3294               if (maxPadding == UNSET) {
 3295                   maxPadding = 0;
 3296               }
 3297               if (lastSize != UNSET) {
 3298                   size += Math.min(maxPadding, lastSize);
 3299               }
 3300           }
 3301   
 3302           private int calculatePadding(LayoutStyle p, int position,
 3303                   ComponentSpring source,
 3304                   ComponentSpring target) {
 3305               int delta = target.getOrigin() - (source.getOrigin() +
 3306                       source.getSize());
 3307               if (delta >= 0) {
 3308                   int padding;
 3309                   if ((source.getComponent() instanceof JComponent) &&
 3310                           (target.getComponent() instanceof JComponent)) {
 3311                       padding = p.getPreferredGap(
 3312                               (JComponent)source.getComponent(),
 3313                               (JComponent)target.getComponent(), type, position,
 3314                               host);
 3315                   } else {
 3316                       padding = 10;
 3317                   }
 3318                   if (padding > delta) {
 3319                       size = Math.max(size, padding - delta);
 3320                   }
 3321                   return padding;
 3322               }
 3323               return 0;
 3324           }
 3325   
 3326           public void addTarget(ComponentSpring spring, int axis) {
 3327               int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL;
 3328               if (source != null) {
 3329                   if (areParallelSiblings(source.getComponent(),
 3330                           spring.getComponent(), oAxis)) {
 3331                       addValidTarget(source, spring);
 3332                   }
 3333               } else {
 3334                   Component component = spring.getComponent();
 3335                   for (int counter = sources.size() - 1; counter >= 0;
 3336                            counter--){
 3337                       ComponentSpring source = sources.get(counter);
 3338                       if (areParallelSiblings(source.getComponent(),
 3339                               component, oAxis)) {
 3340                           addValidTarget(source, spring);
 3341                       }
 3342                   }
 3343               }
 3344           }
 3345   
 3346           private void addValidTarget(ComponentSpring source,
 3347                   ComponentSpring target) {
 3348               if (matches == null) {
 3349                   matches = new ArrayList<AutoPreferredGapMatch>(1);
 3350               }
 3351               matches.add(new AutoPreferredGapMatch(source, target));
 3352           }
 3353   
 3354           int calculateMinimumSize(int axis) {
 3355               return size;
 3356           }
 3357   
 3358           int calculatePreferredSize(int axis) {
 3359               if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) {
 3360                   return size;
 3361               }
 3362               return Math.max(size, pref);
 3363           }
 3364   
 3365           int calculateMaximumSize(int axis) {
 3366               if (max >= 0) {
 3367                   return Math.max(getPreferredSize(axis), max);
 3368               }
 3369               return size;
 3370           }
 3371   
 3372           String getMatchDescription() {
 3373               return (matches == null) ? "" : matches.toString();
 3374           }
 3375   
 3376           public String toString() {
 3377               return super.toString() + getMatchDescription();
 3378           }
 3379   
 3380           @Override
 3381           boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
 3382               return treatAutopaddingAsZeroSized;
 3383           }
 3384       }
 3385   
 3386   
 3387       /**
 3388        * Represents two springs that should have autopadding inserted between
 3389        * them.
 3390        */
 3391       private final static class AutoPreferredGapMatch {
 3392           public final ComponentSpring source;
 3393           public final ComponentSpring target;
 3394   
 3395           AutoPreferredGapMatch(ComponentSpring source, ComponentSpring target) {
 3396               this.source = source;
 3397               this.target = target;
 3398           }
 3399   
 3400           private String toString(ComponentSpring spring) {
 3401               return spring.getComponent().getName();
 3402           }
 3403   
 3404           public String toString() {
 3405               return "[" + toString(source) + "-" + toString(target) + "]";
 3406           }
 3407       }
 3408   
 3409   
 3410       /**
 3411        * An extension of AutopaddingSpring used for container level padding.
 3412        */
 3413       private class ContainerAutoPreferredGapSpring extends
 3414               AutoPreferredGapSpring {
 3415           private List<ComponentSpring> targets;
 3416   
 3417           ContainerAutoPreferredGapSpring() {
 3418               super();
 3419               setUserCreated(true);
 3420           }
 3421   
 3422           ContainerAutoPreferredGapSpring(int pref, int max) {
 3423               super(pref, max);
 3424               setUserCreated(true);
 3425           }
 3426   
 3427           public void addTarget(ComponentSpring spring, int axis) {
 3428               if (targets == null) {
 3429                   targets = new ArrayList<ComponentSpring>(1);
 3430               }
 3431               targets.add(spring);
 3432           }
 3433   
 3434           public void calculatePadding(int axis) {
 3435               LayoutStyle p = getLayoutStyle0();
 3436               int maxPadding = 0;
 3437               int position;
 3438               size = 0;
 3439               if (targets != null) {
 3440                   // Leading
 3441                   if (axis == HORIZONTAL) {
 3442                       if (isLeftToRight()) {
 3443                           position = SwingConstants.WEST;
 3444                       } else {
 3445                           position = SwingConstants.EAST;
 3446                       }
 3447                   } else {
 3448                       position = SwingConstants.SOUTH;
 3449                   }
 3450                   for (int i = targets.size() - 1; i >= 0; i--) {
 3451                       ComponentSpring targetSpring = targets.get(i);
 3452                       int padding = 10;
 3453                       if (targetSpring.getComponent() instanceof JComponent) {
 3454                           padding = p.getContainerGap(
 3455                                   (JComponent)targetSpring.getComponent(),
 3456                                   position, host);
 3457                           maxPadding = Math.max(padding, maxPadding);
 3458                           padding -= targetSpring.getOrigin();
 3459                       } else {
 3460                           maxPadding = Math.max(padding, maxPadding);
 3461                       }
 3462                       size = Math.max(size, padding);
 3463                   }
 3464               } else {
 3465                   // Trailing
 3466                   if (axis == HORIZONTAL) {
 3467                       if (isLeftToRight()) {
 3468                           position = SwingConstants.EAST;
 3469                       } else {
 3470                           position = SwingConstants.WEST;
 3471                       }
 3472                   } else {
 3473                       position = SwingConstants.SOUTH;
 3474                   }
 3475                   if (sources != null) {
 3476                       for (int i = sources.size() - 1; i >= 0; i--) {
 3477                           ComponentSpring sourceSpring = sources.get(i);
 3478                           maxPadding = Math.max(maxPadding,
 3479                                   updateSize(p, sourceSpring, position));
 3480                       }
 3481                   } else if (source != null) {
 3482                       maxPadding = updateSize(p, source, position);
 3483                   }
 3484               }
 3485               if (lastSize != UNSET) {
 3486                   size += Math.min(maxPadding, lastSize);
 3487               }
 3488           }
 3489   
 3490           private int updateSize(LayoutStyle p, ComponentSpring sourceSpring,
 3491                   int position) {
 3492               int padding = 10;
 3493               if (sourceSpring.getComponent() instanceof JComponent) {
 3494                   padding = p.getContainerGap(
 3495                           (JComponent)sourceSpring.getComponent(), position,
 3496                           host);
 3497               }
 3498               int delta = Math.max(0, getParent().getSize() -
 3499                       sourceSpring.getSize() - sourceSpring.getOrigin());
 3500               size = Math.max(size, padding - delta);
 3501               return padding;
 3502           }
 3503   
 3504           String getMatchDescription() {
 3505               if (targets != null) {
 3506                   return "leading: " + targets.toString();
 3507               }
 3508               if (sources != null) {
 3509                   return "trailing: " + sources.toString();
 3510               }
 3511               return "--";
 3512           }
 3513       }
 3514   
 3515   
 3516       // LinkInfo contains the set of ComponentInfosthat are linked along a
 3517       // particular axis.
 3518       private static class LinkInfo {
 3519           private final int axis;
 3520           private final List<ComponentInfo> linked;
 3521           private int size;
 3522   
 3523           LinkInfo(int axis) {
 3524               linked = new ArrayList<ComponentInfo>();
 3525               size = UNSET;
 3526               this.axis = axis;
 3527           }
 3528   
 3529           public void add(ComponentInfo child) {
 3530               LinkInfo childMaster = child.getLinkInfo(axis, false);
 3531               if (childMaster == null) {
 3532                   linked.add(child);
 3533                   child.setLinkInfo(axis, this);
 3534               } else if (childMaster != this) {
 3535                   linked.addAll(childMaster.linked);
 3536                   for (ComponentInfo childInfo : childMaster.linked) {
 3537                       childInfo.setLinkInfo(axis, this);
 3538                   }
 3539               }
 3540               clearCachedSize();
 3541           }
 3542   
 3543           public void remove(ComponentInfo info) {
 3544               linked.remove(info);
 3545               info.setLinkInfo(axis, null);
 3546               if (linked.size() == 1) {
 3547                   linked.get(0).setLinkInfo(axis, null);
 3548               }
 3549               clearCachedSize();
 3550           }
 3551   
 3552           public void clearCachedSize() {
 3553               size = UNSET;
 3554           }
 3555   
 3556           public int getSize(int axis) {
 3557               if (size == UNSET) {
 3558                   size = calculateLinkedSize(axis);
 3559               }
 3560               return size;
 3561           }
 3562   
 3563           private int calculateLinkedSize(int axis) {
 3564               int size = 0;
 3565               for (ComponentInfo info : linked) {
 3566                   ComponentSpring spring;
 3567                   if (axis == HORIZONTAL) {
 3568                       spring = info.horizontalSpring;
 3569                   } else {
 3570                       assert (axis == VERTICAL);
 3571                       spring = info.verticalSpring;
 3572                   }
 3573                   size = Math.max(size,
 3574                           spring.calculateNonlinkedPreferredSize(axis));
 3575               }
 3576               return size;
 3577           }
 3578       }
 3579   
 3580       /**
 3581        * Tracks the horizontal/vertical Springs for a Component.
 3582        * This class is also used to handle Springs that have their sizes
 3583        * linked.
 3584        */
 3585       private class ComponentInfo {
 3586           // Component being layed out
 3587           private Component component;
 3588   
 3589           ComponentSpring horizontalSpring;
 3590           ComponentSpring verticalSpring;
 3591   
 3592           // If the component's size is linked to other components, the
 3593           // horizontalMaster and/or verticalMaster reference the group of
 3594           // linked components.
 3595           private LinkInfo horizontalMaster;
 3596           private LinkInfo verticalMaster;
 3597   
 3598           private boolean visible;
 3599           private Boolean honorsVisibility;
 3600   
 3601           ComponentInfo(Component component) {
 3602               this.component = component;
 3603               updateVisibility();
 3604           }
 3605   
 3606           public void dispose() {
 3607               // Remove horizontal/vertical springs
 3608               removeSpring(horizontalSpring);
 3609               horizontalSpring = null;
 3610               removeSpring(verticalSpring);
 3611               verticalSpring = null;
 3612               // Clean up links
 3613               if (horizontalMaster != null) {
 3614                   horizontalMaster.remove(this);
 3615               }
 3616               if (verticalMaster != null) {
 3617                   verticalMaster.remove(this);
 3618               }
 3619           }
 3620   
 3621           void setHonorsVisibility(Boolean honorsVisibility) {
 3622               this.honorsVisibility = honorsVisibility;
 3623           }
 3624   
 3625           private void removeSpring(Spring spring) {
 3626               if (spring != null) {
 3627                   ((Group)spring.getParent()).springs.remove(spring);
 3628               }
 3629           }
 3630   
 3631           public boolean isVisible() {
 3632               return visible;
 3633           }
 3634   
 3635           /**
 3636            * Updates the cached visibility.
 3637            *
 3638            * @return true if the visibility changed
 3639            */
 3640           boolean updateVisibility() {
 3641               boolean honorsVisibility;
 3642               if (this.honorsVisibility == null) {
 3643                   honorsVisibility = GroupLayout.this.getHonorsVisibility();
 3644               } else {
 3645                   honorsVisibility = this.honorsVisibility;
 3646               }
 3647               boolean newVisible = (honorsVisibility) ?
 3648                   component.isVisible() : true;
 3649               if (visible != newVisible) {
 3650                   visible = newVisible;
 3651                   return true;
 3652               }
 3653               return false;
 3654           }
 3655   
 3656           public void setBounds(Insets insets, int parentWidth, boolean ltr) {
 3657               int x = horizontalSpring.getOrigin();
 3658               int w = horizontalSpring.getSize();
 3659               int y = verticalSpring.getOrigin();
 3660               int h = verticalSpring.getSize();
 3661   
 3662               if (!ltr) {
 3663                   x = parentWidth - x - w;
 3664               }
 3665               component.setBounds(x + insets.left, y + insets.top, w, h);
 3666           }
 3667   
 3668           public void setComponent(Component component) {
 3669               this.component = component;
 3670               if (horizontalSpring != null) {
 3671                   horizontalSpring.setComponent(component);
 3672               }
 3673               if (verticalSpring != null) {
 3674                   verticalSpring.setComponent(component);
 3675               }
 3676           }
 3677   
 3678           public Component getComponent() {
 3679               return component;
 3680           }
 3681   
 3682           /**
 3683            * Returns true if this component has its size linked to
 3684            * other components.
 3685            */
 3686           public boolean isLinked(int axis) {
 3687               if (axis == HORIZONTAL) {
 3688                   return horizontalMaster != null;
 3689               }
 3690               assert (axis == VERTICAL);
 3691               return (verticalMaster != null);
 3692           }
 3693   
 3694           private void setLinkInfo(int axis, LinkInfo linkInfo) {
 3695               if (axis == HORIZONTAL) {
 3696                   horizontalMaster = linkInfo;
 3697               } else {
 3698                   assert (axis == VERTICAL);
 3699                   verticalMaster = linkInfo;
 3700               }
 3701           }
 3702   
 3703           public LinkInfo getLinkInfo(int axis) {
 3704               return getLinkInfo(axis, true);
 3705           }
 3706   
 3707           private LinkInfo getLinkInfo(int axis, boolean create) {
 3708               if (axis == HORIZONTAL) {
 3709                   if (horizontalMaster == null && create) {
 3710                       // horizontalMaster field is directly set by adding
 3711                       // us to the LinkInfo.
 3712                       new LinkInfo(HORIZONTAL).add(this);
 3713                   }
 3714                   return horizontalMaster;
 3715               } else {
 3716                   assert (axis == VERTICAL);
 3717                   if (verticalMaster == null && create) {
 3718                       // verticalMaster field is directly set by adding
 3719                       // us to the LinkInfo.
 3720                       new LinkInfo(VERTICAL).add(this);
 3721                   }
 3722                   return verticalMaster;
 3723               }
 3724           }
 3725   
 3726           public void clearCachedSize() {
 3727               if (horizontalMaster != null) {
 3728                   horizontalMaster.clearCachedSize();
 3729               }
 3730               if (verticalMaster != null) {
 3731                   verticalMaster.clearCachedSize();
 3732               }
 3733           }
 3734   
 3735           int getLinkSize(int axis, int type) {
 3736               if (axis == HORIZONTAL) {
 3737                   return horizontalMaster.getSize(axis);
 3738               } else {
 3739                   assert (axis == VERTICAL);
 3740                   return verticalMaster.getSize(axis);
 3741               }
 3742           }
 3743   
 3744       }
 3745   }

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