Path: blob/master/src/java.desktop/share/classes/javax/swing/GroupLayout.java
41153 views
/*1* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package javax.swing;2526import java.awt.Component;27import java.awt.Container;28import java.awt.Dimension;29import java.awt.Insets;30import java.awt.LayoutManager2;31import java.util.*;32import static java.awt.Component.BaselineResizeBehavior;33import static javax.swing.LayoutStyle.ComponentPlacement;34import static javax.swing.SwingConstants.HORIZONTAL;35import static javax.swing.SwingConstants.VERTICAL;3637/**38* {@code GroupLayout} is a {@code LayoutManager} that hierarchically39* groups components in order to position them in a {@code Container}.40* {@code GroupLayout} is intended for use by builders, but may be41* hand-coded as well.42* Grouping is done by instances of the {@link Group Group} class. {@code43* GroupLayout} supports two types of groups. A sequential group44* positions its child elements sequentially, one after another. A45* parallel group aligns its child elements in one of four ways.46* <p>47* Each group may contain any number of elements, where an element is48* a {@code Group}, {@code Component}, or gap. A gap can be thought49* of as an invisible component with a minimum, preferred and maximum50* size. In addition {@code GroupLayout} supports a preferred gap,51* whose value comes from {@code LayoutStyle}.52* <p>53* Elements are similar to a spring. Each element has a range as54* specified by a minimum, preferred and maximum. Gaps have either a55* developer-specified range, or a range determined by {@code56* LayoutStyle}. The range for {@code Component}s is determined from57* the {@code Component}'s {@code getMinimumSize}, {@code58* getPreferredSize} and {@code getMaximumSize} methods. In addition,59* when adding {@code Component}s you may specify a particular range60* to use instead of that from the component. The range for a {@code61* Group} is determined by the type of group. A {@code ParallelGroup}'s62* range is the maximum of the ranges of its elements. A {@code63* SequentialGroup}'s range is the sum of the ranges of its elements.64* <p>65* {@code GroupLayout} treats each axis independently. That is, there66* is a group representing the horizontal axis, and a group67* representing the vertical axis. The horizontal group is68* responsible for determining the minimum, preferred and maximum size69* along the horizontal axis as well as setting the x and width of the70* components contained in it. The vertical group is responsible for71* determining the minimum, preferred and maximum size along the72* vertical axis as well as setting the y and height of the73* components contained in it. Each {@code Component} must exist in both74* a horizontal and vertical group, otherwise an {@code IllegalStateException}75* is thrown during layout, or when the minimum, preferred or76* maximum size is requested.77* <p>78* The following diagram shows a sequential group along the horizontal79* axis. The sequential group contains three components. A parallel group80* was used along the vertical axis.81* <p style="text-align:center">82* <img src="doc-files/groupLayout.1.gif" alt="Sequential group along the horizontal axis in three components">83* <p>84* To reinforce that each axis is treated independently the diagram shows85* the range of each group and element along each axis. The86* range of each component has been projected onto the axes,87* and the groups are rendered in blue (horizontal) and red (vertical).88* For readability there is a gap between each of the elements in the89* sequential group.90* <p>91* The sequential group along the horizontal axis is rendered as a solid92* blue line. Notice the sequential group is the sum of the children elements93* it contains.94* <p>95* Along the vertical axis the parallel group is the maximum of the height96* of each of the components. As all three components have the same height,97* the parallel group has the same height.98* <p>99* The following diagram shows the same three components, but with the100* parallel group along the horizontal axis and the sequential group along101* the vertical axis.102*103* <p style="text-align:center">104* <img src="doc-files/groupLayout.2.gif" alt="Sequential group along the vertical axis in three components">105* <p>106* As {@code c1} is the largest of the three components, the parallel107* group is sized to {@code c1}. As {@code c2} and {@code c3} are smaller108* than {@code c1} they are aligned based on the alignment specified109* for the component (if specified) or the default alignment of the110* parallel group. In the diagram {@code c2} and {@code c3} were created111* with an alignment of {@code LEADING}. If the component orientation were112* right-to-left then {@code c2} and {@code c3} would be positioned on113* the opposite side.114* <p>115* The following diagram shows a sequential group along both the horizontal116* and vertical axis.117* <p style="text-align:center">118* <img src="doc-files/groupLayout.3.gif" alt="Sequential group along both the horizontal and vertical axis in three components">119* <p>120* {@code GroupLayout} provides the ability to insert gaps between121* {@code Component}s. The size of the gap is determined by an122* instance of {@code LayoutStyle}. This may be turned on using the123* {@code setAutoCreateGaps} method. Similarly, you may use124* the {@code setAutoCreateContainerGaps} method to insert gaps125* between components that touch the edge of the parent container and the126* container.127* <p>128* The following builds a panel consisting of two labels in129* one column, followed by two textfields in the next column:130* <pre>131* JComponent panel = ...;132* GroupLayout layout = new GroupLayout(panel);133* panel.setLayout(layout);134*135* // Turn on automatically adding gaps between components136* layout.setAutoCreateGaps(true);137*138* // Turn on automatically creating gaps between components that touch139* // the edge of the container and the container.140* layout.setAutoCreateContainerGaps(true);141*142* // Create a sequential group for the horizontal axis.143*144* GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();145*146* // The sequential group in turn contains two parallel groups.147* // One parallel group contains the labels, the other the text fields.148* // Putting the labels in a parallel group along the horizontal axis149* // positions them at the same x location.150* //151* // Variable indentation is used to reinforce the level of grouping.152* hGroup.addGroup(layout.createParallelGroup().153* addComponent(label1).addComponent(label2));154* hGroup.addGroup(layout.createParallelGroup().155* addComponent(tf1).addComponent(tf2));156* layout.setHorizontalGroup(hGroup);157*158* // Create a sequential group for the vertical axis.159* GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();160*161* // The sequential group contains two parallel groups that align162* // the contents along the baseline. The first parallel group contains163* // the first label and text field, and the second parallel group contains164* // the second label and text field. By using a sequential group165* // the labels and text fields are positioned vertically after one another.166* vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).167* addComponent(label1).addComponent(tf1));168* vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).169* addComponent(label2).addComponent(tf2));170* layout.setVerticalGroup(vGroup);171* </pre>172* <p>173* When run the following is produced.174* <p style="text-align:center">175* <img src="doc-files/groupLayout.example.png" alt="Produced horizontal/vertical form">176* <p>177* This layout consists of the following.178* <ul><li>The horizontal axis consists of a sequential group containing two179* parallel groups. The first parallel group contains the labels,180* and the second parallel group contains the text fields.181* <li>The vertical axis consists of a sequential group182* containing two parallel groups. The parallel groups are configured183* to align their components along the baseline. The first parallel184* group contains the first label and first text field, and185* the second group consists of the second label and second186* text field.187* </ul>188* There are a couple of things to notice in this code:189* <ul>190* <li>You need not explicitly add the components to the container; this191* is indirectly done by using one of the {@code add} methods of192* {@code Group}.193* <li>The various {@code add} methods return194* the caller. This allows for easy chaining of invocations. For195* example, {@code group.addComponent(label1).addComponent(label2);} is196* equivalent to197* {@code group.addComponent(label1); group.addComponent(label2);}.198* <li>There are no public constructors for {@code Group}s; instead199* use the create methods of {@code GroupLayout}.200* </ul>201*202* @author Tomas Pavek203* @author Jan Stola204* @author Scott Violet205* @since 1.6206*/207public class GroupLayout implements LayoutManager2 {208// Used in size calculations209private static final int MIN_SIZE = 0;210211private static final int PREF_SIZE = 1;212213private static final int MAX_SIZE = 2;214215// Used by prepare, indicates min, pref or max isn't going to be used.216private static final int SPECIFIC_SIZE = 3;217218private static final int UNSET = Integer.MIN_VALUE;219220// Maximum spring size constrain to avoid integer overflow221private static final int INFINITE = Integer.MAX_VALUE >> 1;222223/**224* Indicates the size from the component or gap should be used for a225* particular range value.226*227* @see Group228*/229public static final int DEFAULT_SIZE = -1;230231/**232* Indicates the preferred size from the component or gap should233* be used for a particular range value.234*235* @see Group236*/237public static final int PREFERRED_SIZE = -2;238239// Whether or not we automatically try and create the preferred240// padding between components.241private boolean autocreatePadding;242243// Whether or not we automatically try and create the preferred244// padding between components the touch the edge of the container and245// the container.246private boolean autocreateContainerPadding;247248/**249* Group responsible for layout along the horizontal axis. This is NOT250* the user specified group, use getHorizontalGroup to dig that out.251*/252private Group horizontalGroup;253254/**255* Group responsible for layout along the vertical axis. This is NOT256* the user specified group, use getVerticalGroup to dig that out.257*/258private Group verticalGroup;259260// Maps from Component to ComponentInfo. This is used for tracking261// information specific to a Component.262private Map<Component,ComponentInfo> componentInfos;263264// Container we're doing layout for.265private Container host;266267// Used by areParallelSiblings, cached to avoid excessive garbage.268private Set<Spring> tmpParallelSet;269270// Indicates Springs have changed in some way since last change.271private boolean springsChanged;272273// Indicates invalidateLayout has been invoked.274private boolean isValid;275276// Whether or not any preferred padding (or container padding) springs277// exist278private boolean hasPreferredPaddingSprings;279280/**281* The LayoutStyle instance to use, if null the sharedInstance is used.282*/283private LayoutStyle layoutStyle;284285/**286* If true, components that are not visible are treated as though they287* aren't there.288*/289private boolean honorsVisibility;290291292/**293* Enumeration of the possible ways {@code ParallelGroup} can align294* its children.295*296* @see #createParallelGroup(Alignment)297* @since 1.6298*/299public enum Alignment {300/**301* Indicates the elements should be302* aligned to the origin. For the horizontal axis with a left to303* right orientation this means aligned to the left edge. For the304* vertical axis leading means aligned to the top edge.305*306* @see #createParallelGroup(Alignment)307*/308LEADING,309310/**311* Indicates the elements should be aligned to the end of the312* region. For the horizontal axis with a left to right313* orientation this means aligned to the right edge. For the314* vertical axis trailing means aligned to the bottom edge.315*316* @see #createParallelGroup(Alignment)317*/318TRAILING,319320/**321* Indicates the elements should be centered in322* the region.323*324* @see #createParallelGroup(Alignment)325*/326CENTER,327328/**329* Indicates the elements should be aligned along330* their baseline.331*332* @see #createParallelGroup(Alignment)333* @see #createBaselineGroup(boolean,boolean)334*/335BASELINE336}337338339private static void checkSize(int min, int pref, int max,340boolean isComponentSpring) {341checkResizeType(min, isComponentSpring);342if (!isComponentSpring && pref < 0) {343throw new IllegalArgumentException("Pref must be >= 0");344} else if (isComponentSpring) {345checkResizeType(pref, true);346}347checkResizeType(max, isComponentSpring);348checkLessThan(min, pref);349checkLessThan(pref, max);350}351352private static void checkResizeType(int type, boolean isComponentSpring) {353if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE &&354type != PREFERRED_SIZE) ||355(!isComponentSpring && type != PREFERRED_SIZE))) {356throw new IllegalArgumentException("Invalid size");357}358}359360private static void checkLessThan(int min, int max) {361if (min >= 0 && max >= 0 && min > max) {362throw new IllegalArgumentException(363"Following is not met: min<=pref<=max");364}365}366367/**368* Creates a {@code GroupLayout} for the specified {@code Container}.369*370* @param host the {@code Container} the {@code GroupLayout} is371* the {@code LayoutManager} for372* @throws IllegalArgumentException if host is {@code null}373*/374public GroupLayout(Container host) {375if (host == null) {376throw new IllegalArgumentException("Container must be non-null");377}378honorsVisibility = true;379this.host = host;380setHorizontalGroup(createParallelGroup(Alignment.LEADING, true));381setVerticalGroup(createParallelGroup(Alignment.LEADING, true));382componentInfos = new HashMap<Component,ComponentInfo>();383tmpParallelSet = new HashSet<Spring>();384}385386/**387* Sets whether component visibility is considered when sizing and388* positioning components. A value of {@code true} indicates that389* non-visible components should not be treated as part of the390* layout. A value of {@code false} indicates that components should be391* positioned and sized regardless of visibility.392* <p>393* A value of {@code false} is useful when the visibility of components394* is dynamically adjusted and you don't want surrounding components and395* the sizing to change.396* <p>397* The specified value is used for components that do not have an398* explicit visibility specified.399* <p>400* The default is {@code true}.401*402* @param honorsVisibility whether component visibility is considered when403* sizing and positioning components404* @see #setHonorsVisibility(Component,Boolean)405*/406public void setHonorsVisibility(boolean honorsVisibility) {407if (this.honorsVisibility != honorsVisibility) {408this.honorsVisibility = honorsVisibility;409springsChanged = true;410isValid = false;411invalidateHost();412}413}414415/**416* Returns whether component visibility is considered when sizing and417* positioning components.418*419* @return whether component visibility is considered when sizing and420* positioning components421*/422public boolean getHonorsVisibility() {423return honorsVisibility;424}425426/**427* Sets whether the component's visibility is considered for428* sizing and positioning. A value of {@code Boolean.TRUE}429* indicates that if {@code component} is not visible it should430* not be treated as part of the layout. A value of {@code false}431* indicates that {@code component} is positioned and sized432* regardless of its visibility. A value of {@code null}433* indicates the value specified by the single argument method {@code434* setHonorsVisibility} should be used.435* <p>436* If {@code component} is not a child of the {@code Container} this437* {@code GroupLayout} is managing, it will be added to the438* {@code Container}.439*440* @param component the component441* @param honorsVisibility whether visibility of this {@code component} should be442* considered for sizing and positioning443* @throws IllegalArgumentException if {@code component} is {@code null}444* @see #setHonorsVisibility(Component,Boolean)445*/446public void setHonorsVisibility(Component component,447Boolean honorsVisibility) {448if (component == null) {449throw new IllegalArgumentException("Component must be non-null");450}451getComponentInfo(component).setHonorsVisibility(honorsVisibility);452springsChanged = true;453isValid = false;454invalidateHost();455}456457/**458* Sets whether a gap between components should automatically be459* created. For example, if this is {@code true} and you add two460* components to a {@code SequentialGroup} a gap between the461* two components is automatically be created. The default is462* {@code false}.463*464* @param autoCreatePadding whether a gap between components is465* automatically created466*/467public void setAutoCreateGaps(boolean autoCreatePadding) {468if (this.autocreatePadding != autoCreatePadding) {469this.autocreatePadding = autoCreatePadding;470invalidateHost();471}472}473474/**475* Returns {@code true} if gaps between components are automatically476* created.477*478* @return {@code true} if gaps between components are automatically479* created480*/481public boolean getAutoCreateGaps() {482return autocreatePadding;483}484485/**486* Sets whether a gap between the container and components that487* touch the border of the container should automatically be488* created. The default is {@code false}.489*490* @param autoCreateContainerPadding whether a gap between the container and491* components that touch the border of the container should492* automatically be created493*/494public void setAutoCreateContainerGaps(boolean autoCreateContainerPadding){495if (this.autocreateContainerPadding != autoCreateContainerPadding) {496this.autocreateContainerPadding = autoCreateContainerPadding;497horizontalGroup = createTopLevelGroup(getHorizontalGroup());498verticalGroup = createTopLevelGroup(getVerticalGroup());499invalidateHost();500}501}502503/**504* Returns {@code true} if gaps between the container and components that505* border the container are automatically created.506*507* @return {@code true} if gaps between the container and components that508* border the container are automatically created509*/510public boolean getAutoCreateContainerGaps() {511return autocreateContainerPadding;512}513514/**515* Sets the {@code Group} that positions and sizes516* components along the horizontal axis.517*518* @param group the {@code Group} that positions and sizes519* components along the horizontal axis520* @throws IllegalArgumentException if group is {@code null}521*/522public void setHorizontalGroup(Group group) {523if (group == null) {524throw new IllegalArgumentException("Group must be non-null");525}526horizontalGroup = createTopLevelGroup(group);527invalidateHost();528}529530/**531* Returns the {@code Group} that positions and sizes components532* along the horizontal axis.533*534* @return the {@code Group} responsible for positioning and535* sizing component along the horizontal axis536*/537private Group getHorizontalGroup() {538int index = 0;539if (horizontalGroup.springs.size() > 1) {540index = 1;541}542return (Group)horizontalGroup.springs.get(index);543}544545/**546* Sets the {@code Group} that positions and sizes547* components along the vertical axis.548*549* @param group the {@code Group} that positions and sizes550* components along the vertical axis551* @throws IllegalArgumentException if group is {@code null}552*/553public void setVerticalGroup(Group group) {554if (group == null) {555throw new IllegalArgumentException("Group must be non-null");556}557verticalGroup = createTopLevelGroup(group);558invalidateHost();559}560561/**562* Returns the {@code Group} that positions and sizes components563* along the vertical axis.564*565* @return the {@code Group} responsible for positioning and566* sizing component along the vertical axis567*/568private Group getVerticalGroup() {569int index = 0;570if (verticalGroup.springs.size() > 1) {571index = 1;572}573return (Group)verticalGroup.springs.get(index);574}575576/**577* Wraps the user specified group in a sequential group. If578* container gaps should be generated the necessary springs are579* added.580*/581private Group createTopLevelGroup(Group specifiedGroup) {582SequentialGroup group = createSequentialGroup();583if (getAutoCreateContainerGaps()) {584group.addSpring(new ContainerAutoPreferredGapSpring());585group.addGroup(specifiedGroup);586group.addSpring(new ContainerAutoPreferredGapSpring());587} else {588group.addGroup(specifiedGroup);589}590return group;591}592593/**594* Creates and returns a {@code SequentialGroup}.595*596* @return a new {@code SequentialGroup}597*/598public SequentialGroup createSequentialGroup() {599return new SequentialGroup();600}601602/**603* Creates and returns a {@code ParallelGroup} with an alignment of604* {@code Alignment.LEADING}. This is a cover method for the more605* general {@code createParallelGroup(Alignment)} method.606*607* @return a new {@code ParallelGroup}608* @see #createParallelGroup(Alignment)609*/610public ParallelGroup createParallelGroup() {611return createParallelGroup(Alignment.LEADING);612}613614/**615* Creates and returns a {@code ParallelGroup} with the specified616* alignment. This is a cover method for the more general {@code617* createParallelGroup(Alignment,boolean)} method with {@code true}618* supplied for the second argument.619*620* @param alignment the alignment for the elements of the group621* @throws IllegalArgumentException if {@code alignment} is {@code null}622* @return a new {@code ParallelGroup}623* @see #createBaselineGroup624* @see ParallelGroup625*/626public ParallelGroup createParallelGroup(Alignment alignment) {627return createParallelGroup(alignment, true);628}629630/**631* Creates and returns a {@code ParallelGroup} with the specified632* alignment and resize behavior. The {@code633* alignment} argument specifies how children elements are634* positioned that do not fill the group. For example, if a {@code635* ParallelGroup} with an alignment of {@code TRAILING} is given636* 100 and a child only needs 50, the child is637* positioned at the position 50 (with a component orientation of638* left-to-right).639* <p>640* Baseline alignment is only useful when used along the vertical641* axis. A {@code ParallelGroup} created with a baseline alignment642* along the horizontal axis is treated as {@code LEADING}.643* <p>644* Refer to {@link GroupLayout.ParallelGroup ParallelGroup} for details on645* the behavior of baseline groups.646*647* @param alignment the alignment for the elements of the group648* @param resizable {@code true} if the group is resizable; if the group649* is not resizable the preferred size is used for the650* minimum and maximum size of the group651* @throws IllegalArgumentException if {@code alignment} is {@code null}652* @return a new {@code ParallelGroup}653* @see #createBaselineGroup654* @see GroupLayout.ParallelGroup655*/656public ParallelGroup createParallelGroup(Alignment alignment,657boolean resizable){658if (alignment == null) {659throw new IllegalArgumentException("alignment must be non null");660}661662if (alignment == Alignment.BASELINE) {663return new BaselineGroup(resizable);664}665return new ParallelGroup(alignment, resizable);666}667668/**669* Creates and returns a {@code ParallelGroup} that aligns its670* elements along the baseline.671*672* @param resizable whether the group is resizable673* @param anchorBaselineToTop whether the baseline is anchored to674* the top or bottom of the group675* @return the {@code ParallelGroup}676* @see #createBaselineGroup677* @see ParallelGroup678*/679public ParallelGroup createBaselineGroup(boolean resizable,680boolean anchorBaselineToTop) {681return new BaselineGroup(resizable, anchorBaselineToTop);682}683684/**685* Forces the specified components to have the same size686* regardless of their preferred, minimum or maximum sizes. Components that687* are linked are given the maximum of the preferred size of each of688* the linked components. For example, if you link two components with689* a preferred width of 10 and 20, both components are given a width of 20.690* <p>691* This can be used multiple times to force any number of692* components to share the same size.693* <p>694* Linked Components are not be resizable.695*696* @param components the {@code Component}s that are to have the same size697* @throws IllegalArgumentException if {@code components} is698* {@code null}, or contains {@code null}699* @see #linkSize(int,Component[])700*/701public void linkSize(Component... components) {702linkSize(SwingConstants.HORIZONTAL, components);703linkSize(SwingConstants.VERTICAL, components);704}705706/**707* Forces the specified components to have the same size along the708* specified axis regardless of their preferred, minimum or709* maximum sizes. Components that are linked are given the maximum710* of the preferred size of each of the linked components. For711* example, if you link two components along the horizontal axis712* and the preferred width is 10 and 20, both components are given713* a width of 20.714* <p>715* This can be used multiple times to force any number of716* components to share the same size.717* <p>718* Linked {@code Component}s are not be resizable.719*720* @param axis the axis to link the size along; one of721* {@code SwingConstants.HORIZONTAL} or722* {@code SwingConstants.VERTICAL}723* @param components the {@code Component}s that are to have the same size724* @throws IllegalArgumentException if {@code components} is725* {@code null}, or contains {@code null}; or {@code axis}726* is not {@code SwingConstants.HORIZONTAL} or727* {@code SwingConstants.VERTICAL}728*/729public void linkSize(int axis, Component... components) {730if (components == null) {731throw new IllegalArgumentException("Components must be non-null");732}733for (int counter = components.length - 1; counter >= 0; counter--) {734Component c = components[counter];735if (components[counter] == null) {736throw new IllegalArgumentException(737"Components must be non-null");738}739// Force the component to be added740getComponentInfo(c);741}742int glAxis;743if (axis == SwingConstants.HORIZONTAL) {744glAxis = HORIZONTAL;745} else if (axis == SwingConstants.VERTICAL) {746glAxis = VERTICAL;747} else {748throw new IllegalArgumentException("Axis must be one of " +749"SwingConstants.HORIZONTAL or SwingConstants.VERTICAL");750}751LinkInfo master = getComponentInfo(752components[components.length - 1]).getLinkInfo(glAxis);753for (int counter = components.length - 2; counter >= 0; counter--) {754master.add(getComponentInfo(components[counter]));755}756invalidateHost();757}758759/**760* Replaces an existing component with a new one.761*762* @param existingComponent the component that should be removed763* and replaced with {@code newComponent}764* @param newComponent the component to put in765* {@code existingComponent}'s place766* @throws IllegalArgumentException if either of the components are767* {@code null} or {@code existingComponent} is not being managed768* by this layout manager769*/770public void replace(Component existingComponent, Component newComponent) {771if (existingComponent == null || newComponent == null) {772throw new IllegalArgumentException("Components must be non-null");773}774// Make sure all the components have been registered, otherwise we may775// not update the correct Springs.776if (springsChanged) {777registerComponents(horizontalGroup, HORIZONTAL);778registerComponents(verticalGroup, VERTICAL);779}780ComponentInfo info = componentInfos.remove(existingComponent);781if (info == null) {782throw new IllegalArgumentException("Component must already exist");783}784host.remove(existingComponent);785if (newComponent.getParent() != host) {786host.add(newComponent);787}788info.setComponent(newComponent);789componentInfos.put(newComponent, info);790invalidateHost();791}792793/**794* Sets the {@code LayoutStyle} used to calculate the preferred795* gaps between components. A value of {@code null} indicates the796* shared instance of {@code LayoutStyle} should be used.797*798* @param layoutStyle the {@code LayoutStyle} to use799* @see LayoutStyle800*/801public void setLayoutStyle(LayoutStyle layoutStyle) {802this.layoutStyle = layoutStyle;803invalidateHost();804}805806/**807* Returns the {@code LayoutStyle} used for calculating the preferred808* gap between components. This returns the value specified to809* {@code setLayoutStyle}, which may be {@code null}.810*811* @return the {@code LayoutStyle} used for calculating the preferred812* gap between components813*/814public LayoutStyle getLayoutStyle() {815return layoutStyle;816}817818private LayoutStyle getLayoutStyle0() {819LayoutStyle layoutStyle = getLayoutStyle();820if (layoutStyle == null) {821layoutStyle = LayoutStyle.getInstance();822}823return layoutStyle;824}825826private void invalidateHost() {827if (host instanceof JComponent) {828((JComponent)host).revalidate();829} else {830host.invalidate();831}832host.repaint();833}834835//836// LayoutManager837//838/**839* Notification that a {@code Component} has been added to840* the parent container. You should not invoke this method841* directly, instead you should use one of the {@code Group}842* methods to add a {@code Component}.843*844* @param name the string to be associated with the component845* @param component the {@code Component} to be added846*/847public void addLayoutComponent(String name, Component component) {848}849850/**851* Notification that a {@code Component} has been removed from852* the parent container. You should not invoke this method853* directly, instead invoke {@code remove} on the parent854* {@code Container}.855*856* @param component the component to be removed857* @see java.awt.Component#remove858*/859public void removeLayoutComponent(Component component) {860ComponentInfo info = componentInfos.remove(component);861if (info != null) {862info.dispose();863springsChanged = true;864isValid = false;865}866}867868/**869* Returns the preferred size for the specified container.870*871* @param parent the container to return the preferred size for872* @return the preferred size for {@code parent}873* @throws IllegalArgumentException if {@code parent} is not874* the same {@code Container} this was created with875* @throws IllegalStateException if any of the components added to876* this layout are not in both a horizontal and vertical group877* @see java.awt.Container#getPreferredSize878*/879public Dimension preferredLayoutSize(Container parent) {880checkParent(parent);881prepare(PREF_SIZE);882return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),883verticalGroup.getPreferredSize(VERTICAL));884}885886/**887* Returns the minimum size for the specified container.888*889* @param parent the container to return the size for890* @return the minimum size for {@code parent}891* @throws IllegalArgumentException if {@code parent} is not892* the same {@code Container} that this was created with893* @throws IllegalStateException if any of the components added to894* this layout are not in both a horizontal and vertical group895* @see java.awt.Container#getMinimumSize896*/897public Dimension minimumLayoutSize(Container parent) {898checkParent(parent);899prepare(MIN_SIZE);900return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),901verticalGroup.getMinimumSize(VERTICAL));902}903904/**905* Lays out the specified container.906*907* @param parent the container to be laid out908* @throws IllegalStateException if any of the components added to909* this layout are not in both a horizontal and vertical group910*/911public void layoutContainer(Container parent) {912// Step 1: Prepare for layout.913prepare(SPECIFIC_SIZE);914Insets insets = parent.getInsets();915int width = parent.getWidth() - insets.left - insets.right;916int height = parent.getHeight() - insets.top - insets.bottom;917boolean ltr = isLeftToRight();918if (getAutoCreateGaps() || getAutoCreateContainerGaps() ||919hasPreferredPaddingSprings) {920// Step 2: Calculate autopadding springs921calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0,922width);923calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0,924height);925}926// Step 3: set the size of the groups.927horizontalGroup.setSize(HORIZONTAL, 0, width);928verticalGroup.setSize(VERTICAL, 0, height);929// Step 4: apply the size to the components.930for (ComponentInfo info : componentInfos.values()) {931info.setBounds(insets, width, ltr);932}933}934935//936// LayoutManager2937//938/**939* Notification that a {@code Component} has been added to940* the parent container. You should not invoke this method941* directly, instead you should use one of the {@code Group}942* methods to add a {@code Component}.943*944* @param component the component added945* @param constraints description of where to place the component946*/947public void addLayoutComponent(Component component, Object constraints) {948}949950/**951* Returns the maximum size for the specified container.952*953* @param parent the container to return the size for954* @return the maximum size for {@code parent}955* @throws IllegalArgumentException if {@code parent} is not956* the same {@code Container} that this was created with957* @throws IllegalStateException if any of the components added to958* this layout are not in both a horizontal and vertical group959* @see java.awt.Container#getMaximumSize960*/961public Dimension maximumLayoutSize(Container parent) {962checkParent(parent);963prepare(MAX_SIZE);964return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),965verticalGroup.getMaximumSize(VERTICAL));966}967968/**969* Returns the alignment along the x axis. This specifies how970* the component would like to be aligned relative to other971* components. The value should be a number between 0 and 1972* where 0 represents alignment along the origin, 1 is aligned973* the furthest away from the origin, 0.5 is centered, etc.974*975* @param parent the {@code Container} hosting this {@code LayoutManager}976* @throws IllegalArgumentException if {@code parent} is not977* the same {@code Container} that this was created with978* @return the alignment; this implementation returns {@code .5}979*/980public float getLayoutAlignmentX(Container parent) {981checkParent(parent);982return .5f;983}984985/**986* Returns the alignment along the y axis. This specifies how987* the component would like to be aligned relative to other988* components. The value should be a number between 0 and 1989* where 0 represents alignment along the origin, 1 is aligned990* the furthest away from the origin, 0.5 is centered, etc.991*992* @param parent the {@code Container} hosting this {@code LayoutManager}993* @throws IllegalArgumentException if {@code parent} is not994* the same {@code Container} that this was created with995* @return alignment; this implementation returns {@code .5}996*/997public float getLayoutAlignmentY(Container parent) {998checkParent(parent);999return .5f;1000}10011002/**1003* Invalidates the layout, indicating that if the layout manager1004* has cached information it should be discarded.1005*1006* @param parent the {@code Container} hosting this LayoutManager1007* @throws IllegalArgumentException if {@code parent} is not1008* the same {@code Container} that this was created with1009*/1010public void invalidateLayout(Container parent) {1011checkParent(parent);1012// invalidateLayout is called from Container.invalidate, which1013// does NOT grab the treelock. All other methods do. To make sure1014// there aren't any possible threading problems we grab the tree lock1015// here.1016synchronized(parent.getTreeLock()) {1017isValid = false;1018}1019}10201021private void prepare(int sizeType) {1022boolean visChanged = false;1023// Step 1: If not-valid, clear springs and update visibility.1024if (!isValid) {1025isValid = true;1026horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);1027verticalGroup.setSize(VERTICAL, UNSET, UNSET);1028for (ComponentInfo ci : componentInfos.values()) {1029if (ci.updateVisibility()) {1030visChanged = true;1031}1032ci.clearCachedSize();1033}1034}1035// Step 2: Make sure components are bound to ComponentInfos1036if (springsChanged) {1037registerComponents(horizontalGroup, HORIZONTAL);1038registerComponents(verticalGroup, VERTICAL);1039}1040// Step 3: Adjust the autopadding. This removes existing1041// autopadding, then recalculates where it should go.1042if (springsChanged || visChanged) {1043checkComponents();1044horizontalGroup.removeAutopadding();1045verticalGroup.removeAutopadding();1046if (getAutoCreateGaps()) {1047insertAutopadding(true);1048} else if (hasPreferredPaddingSprings ||1049getAutoCreateContainerGaps()) {1050insertAutopadding(false);1051}1052springsChanged = false;1053}1054// Step 4: (for min/pref/max size calculations only) calculate the1055// autopadding. This invokes for unsetting the calculated values, then1056// recalculating them.1057// If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this1058// step will be done later on.1059if (sizeType != SPECIFIC_SIZE && (getAutoCreateGaps() ||1060getAutoCreateContainerGaps() || hasPreferredPaddingSprings)) {1061calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0);1062calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);1063}1064}10651066private void calculateAutopadding(Group group, int axis, int sizeType,1067int origin, int size) {1068group.unsetAutopadding();1069switch(sizeType) {1070case MIN_SIZE:1071size = group.getMinimumSize(axis);1072break;1073case PREF_SIZE:1074size = group.getPreferredSize(axis);1075break;1076case MAX_SIZE:1077size = group.getMaximumSize(axis);1078break;1079default:1080break;1081}1082group.setSize(axis, origin, size);1083group.calculateAutopadding(axis);1084}10851086private void checkComponents() {1087for (ComponentInfo info : componentInfos.values()) {1088if (info.horizontalSpring == null) {1089throw new IllegalStateException(info.component +1090" is not attached to a horizontal group");1091}1092if (info.verticalSpring == null) {1093throw new IllegalStateException(info.component +1094" is not attached to a vertical group");1095}1096}1097}10981099private void registerComponents(Group group, int axis) {1100List<Spring> springs = group.springs;1101for (int counter = springs.size() - 1; counter >= 0; counter--) {1102Spring spring = springs.get(counter);1103if (spring instanceof ComponentSpring) {1104((ComponentSpring)spring).installIfNecessary(axis);1105} else if (spring instanceof Group) {1106registerComponents((Group)spring, axis);1107}1108}1109}11101111private Dimension adjustSize(int width, int height) {1112Insets insets = host.getInsets();1113return new Dimension(width + insets.left + insets.right,1114height + insets.top + insets.bottom);1115}11161117private void checkParent(Container parent) {1118if (parent != host) {1119throw new IllegalArgumentException(1120"GroupLayout can only be used with one Container at a time");1121}1122}11231124/**1125* Returns the {@code ComponentInfo} for the specified Component,1126* creating one if necessary.1127*/1128private ComponentInfo getComponentInfo(Component component) {1129ComponentInfo info = componentInfos.get(component);1130if (info == null) {1131info = new ComponentInfo(component);1132componentInfos.put(component, info);1133if (component.getParent() != host) {1134host.add(component);1135}1136}1137return info;1138}11391140/**1141* Adjusts the autopadding springs for the horizontal and vertical1142* groups. If {@code insert} is {@code true} this will insert auto padding1143* springs, otherwise this will only adjust the springs that1144* comprise auto preferred padding springs.1145*/1146private void insertAutopadding(boolean insert) {1147horizontalGroup.insertAutopadding(HORIZONTAL,1148new ArrayList<AutoPreferredGapSpring>(1),1149new ArrayList<AutoPreferredGapSpring>(1),1150new ArrayList<ComponentSpring>(1),1151new ArrayList<ComponentSpring>(1), insert);1152verticalGroup.insertAutopadding(VERTICAL,1153new ArrayList<AutoPreferredGapSpring>(1),1154new ArrayList<AutoPreferredGapSpring>(1),1155new ArrayList<ComponentSpring>(1),1156new ArrayList<ComponentSpring>(1), insert);1157}11581159/**1160* Returns {@code true} if the two Components have a common ParallelGroup1161* ancestor along the particular axis.1162*/1163private boolean areParallelSiblings(Component source, Component target,1164int axis) {1165ComponentInfo sourceInfo = getComponentInfo(source);1166ComponentInfo targetInfo = getComponentInfo(target);1167Spring sourceSpring;1168Spring targetSpring;1169if (axis == HORIZONTAL) {1170sourceSpring = sourceInfo.horizontalSpring;1171targetSpring = targetInfo.horizontalSpring;1172} else {1173sourceSpring = sourceInfo.verticalSpring;1174targetSpring = targetInfo.verticalSpring;1175}1176Set<Spring> sourcePath = tmpParallelSet;1177sourcePath.clear();1178Spring spring = sourceSpring.getParent();1179while (spring != null) {1180sourcePath.add(spring);1181spring = spring.getParent();1182}1183spring = targetSpring.getParent();1184while (spring != null) {1185if (sourcePath.contains(spring)) {1186sourcePath.clear();1187while (spring != null) {1188if (spring instanceof ParallelGroup) {1189return true;1190}1191spring = spring.getParent();1192}1193return false;1194}1195spring = spring.getParent();1196}1197sourcePath.clear();1198return false;1199}12001201private boolean isLeftToRight() {1202return host.getComponentOrientation().isLeftToRight();1203}12041205/**1206* Returns a string representation of this {@code GroupLayout}.1207* This method is intended to be used for debugging purposes,1208* and the content and format of the returned string may vary1209* between implementations.1210*1211* @return a string representation of this {@code GroupLayout}1212**/1213public String toString() {1214if (springsChanged) {1215registerComponents(horizontalGroup, HORIZONTAL);1216registerComponents(verticalGroup, VERTICAL);1217}1218StringBuilder sb = new StringBuilder();1219sb.append("HORIZONTAL\n");1220createSpringDescription(sb, horizontalGroup, " ", HORIZONTAL);1221sb.append("\nVERTICAL\n");1222createSpringDescription(sb, verticalGroup, " ", VERTICAL);1223return sb.toString();1224}12251226private void createSpringDescription(StringBuilder sb, Spring spring,1227String indent, int axis) {1228String origin = "";1229String padding = "";1230if (spring instanceof ComponentSpring) {1231ComponentSpring cSpring = (ComponentSpring)spring;1232origin = Integer.toString(cSpring.getOrigin()) + " ";1233String name = cSpring.getComponent().getName();1234if (name != null) {1235origin = "name=" + name + ", ";1236}1237}1238if (spring instanceof AutoPreferredGapSpring) {1239AutoPreferredGapSpring paddingSpring =1240(AutoPreferredGapSpring)spring;1241padding = ", userCreated=" + paddingSpring.getUserCreated() +1242", matches=" + paddingSpring.getMatchDescription();1243}1244sb.append(indent).append(spring.getClass().getName()).append(' ')1245.append(Integer.toHexString(spring.hashCode())).append(' ')1246.append(origin).append(", size=").append(spring.getSize())1247.append(", alignment=").append(spring.getAlignment())1248.append(" prefs=[").append(spring.getMinimumSize(axis))1249.append(' ').append(spring.getPreferredSize(axis)).append(' ')1250.append(spring.getMaximumSize(axis)).append(padding)1251.append("]\n");1252if (spring instanceof Group) {1253List<Spring> springs = ((Group)spring).springs;1254indent += " ";1255for (int counter = 0; counter < springs.size(); counter++) {1256createSpringDescription(sb, springs.get(counter), indent,1257axis);1258}1259}1260}126112621263/**1264* Spring consists of a range: min, pref and max, a value some where in1265* the middle of that, and a location. Spring caches the1266* min/max/pref. If the min/pref/max has internally changes, or needs1267* to be updated you must invoke clear.1268*/1269private abstract class Spring {1270private int size;1271private int min;1272private int max;1273private int pref;1274private Spring parent;12751276private Alignment alignment;12771278Spring() {1279min = pref = max = UNSET;1280}12811282/**1283* Calculates and returns the minimum size.1284*1285* @param axis the axis of layout; one of HORIZONTAL or VERTICAL1286* @return the minimum size1287*/1288abstract int calculateMinimumSize(int axis);12891290/**1291* Calculates and returns the preferred size.1292*1293* @param axis the axis of layout; one of HORIZONTAL or VERTICAL1294* @return the preferred size1295*/1296abstract int calculatePreferredSize(int axis);12971298/**1299* Calculates and returns the minimum size.1300*1301* @param axis the axis of layout; one of HORIZONTAL or VERTICAL1302* @return the minimum size1303*/1304abstract int calculateMaximumSize(int axis);13051306/**1307* Sets the parent of this Spring.1308*/1309void setParent(Spring parent) {1310this.parent = parent;1311}13121313/**1314* Returns the parent of this spring.1315*/1316Spring getParent() {1317return parent;1318}13191320// This is here purely as a convenience for ParallelGroup to avoid1321// having to track alignment separately.1322void setAlignment(Alignment alignment) {1323this.alignment = alignment;1324}13251326/**1327* Alignment for this Spring, this may be null.1328*/1329Alignment getAlignment() {1330return alignment;1331}13321333/**1334* Returns the minimum size.1335*/1336final int getMinimumSize(int axis) {1337if (min == UNSET) {1338min = constrain(calculateMinimumSize(axis));1339}1340return min;1341}13421343/**1344* Returns the preferred size.1345*/1346final int getPreferredSize(int axis) {1347if (pref == UNSET) {1348pref = constrain(calculatePreferredSize(axis));1349}1350return pref;1351}13521353/**1354* Returns the maximum size.1355*/1356final int getMaximumSize(int axis) {1357if (max == UNSET) {1358max = constrain(calculateMaximumSize(axis));1359}1360return max;1361}13621363/**1364* Sets the value and location of the spring. Subclasses1365* will want to invoke super, then do any additional sizing.1366*1367* @param axis HORIZONTAL or VERTICAL1368* @param origin of this Spring1369* @param size of the Spring. If size is UNSET, this invokes1370* clear.1371*/1372void setSize(int axis, int origin, int size) {1373this.size = size;1374if (size == UNSET) {1375unset();1376}1377}13781379/**1380* Resets the cached min/max/pref.1381*/1382void unset() {1383size = min = pref = max = UNSET;1384}13851386/**1387* Returns the current size.1388*/1389int getSize() {1390return size;1391}13921393int constrain(int value) {1394return Math.min(value, INFINITE);1395}13961397int getBaseline() {1398return -1;1399}14001401BaselineResizeBehavior getBaselineResizeBehavior() {1402return BaselineResizeBehavior.OTHER;1403}14041405final boolean isResizable(int axis) {1406int min = getMinimumSize(axis);1407int pref = getPreferredSize(axis);1408return (min != pref || pref != getMaximumSize(axis));1409}14101411/**1412* Returns {@code true} if this spring will ALWAYS have a zero1413* size. This should NOT check the current size, rather it's1414* meant to quickly test if this Spring will always have a1415* zero size.1416*1417* @param treatAutopaddingAsZeroSized if {@code true}, auto padding1418* springs should be treated as having a size of {@code 0}1419* @return {@code true} if this spring will have a zero size,1420* {@code false} otherwise1421*/1422abstract boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized);1423}14241425/**1426* {@code Group} provides the basis for the two types of1427* operations supported by {@code GroupLayout}: laying out1428* components one after another ({@link SequentialGroup SequentialGroup})1429* or aligned ({@link ParallelGroup ParallelGroup}). {@code Group} and1430* its subclasses have no public constructor; to create one use1431* one of {@code createSequentialGroup} or1432* {@code createParallelGroup}. Additionally, taking a {@code Group}1433* created from one {@code GroupLayout} and using it with another1434* will produce undefined results.1435* <p>1436* Various methods in {@code Group} and its subclasses allow you1437* to explicitly specify the range. The arguments to these methods1438* can take two forms, either a value greater than or equal to 0,1439* or one of {@code DEFAULT_SIZE} or {@code PREFERRED_SIZE}. A1440* value greater than or equal to {@code 0} indicates a specific1441* size. {@code DEFAULT_SIZE} indicates the corresponding size1442* from the component should be used. For example, if {@code1443* DEFAULT_SIZE} is passed as the minimum size argument, the1444* minimum size is obtained from invoking {@code getMinimumSize}1445* on the component. Likewise, {@code PREFERRED_SIZE} indicates1446* the value from {@code getPreferredSize} should be used.1447* The following example adds {@code myComponent} to {@code group}1448* with specific values for the range. That is, the minimum is1449* explicitly specified as 100, preferred as 200, and maximum as1450* 300.1451* <pre>1452* group.addComponent(myComponent, 100, 200, 300);1453* </pre>1454* The following example adds {@code myComponent} to {@code group} using1455* a combination of the forms. The minimum size is forced to be the1456* same as the preferred size, the preferred size is determined by1457* using {@code myComponent.getPreferredSize} and the maximum is1458* determined by invoking {@code getMaximumSize} on the component.1459* <pre>1460* group.addComponent(myComponent, GroupLayout.PREFERRED_SIZE,1461* GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE);1462* </pre>1463* <p>1464* Unless otherwise specified all the methods of {@code Group} and1465* its subclasses that allow you to specify a range throw an1466* {@code IllegalArgumentException} if passed an invalid range. An1467* invalid range is one in which any of the values are < 0 and1468* not one of {@code PREFERRED_SIZE} or {@code DEFAULT_SIZE}, or1469* the following is not met (for specific values): {@code min}1470* <= {@code pref} <= {@code max}.1471* <p>1472* Similarly any methods that take a {@code Component} throw a1473* {@code IllegalArgumentException} if passed {@code null} and any methods1474* that take a {@code Group} throw an {@code NullPointerException} if1475* passed {@code null}.1476*1477* @see #createSequentialGroup1478* @see #createParallelGroup1479* @since 1.61480*/1481public abstract class Group extends Spring {1482// private int origin;1483// private int size;1484List<Spring> springs;14851486Group() {1487springs = new ArrayList<Spring>();1488}14891490/**1491* Adds a {@code Group} to this {@code Group}.1492*1493* @param group the {@code Group} to add1494* @return this {@code Group}1495*/1496public Group addGroup(Group group) {1497return addSpring(group);1498}14991500/**1501* Adds a {@code Component} to this {@code Group}.1502*1503* @param component the {@code Component} to add1504* @return this {@code Group}1505*/1506public Group addComponent(Component component) {1507return addComponent(component, DEFAULT_SIZE, DEFAULT_SIZE,1508DEFAULT_SIZE);1509}15101511/**1512* Adds a {@code Component} to this {@code Group}1513* with the specified size.1514*1515* @param component the {@code Component} to add1516* @param min the minimum size or one of {@code DEFAULT_SIZE} or1517* {@code PREFERRED_SIZE}1518* @param pref the preferred size or one of {@code DEFAULT_SIZE} or1519* {@code PREFERRED_SIZE}1520* @param max the maximum size or one of {@code DEFAULT_SIZE} or1521* {@code PREFERRED_SIZE}1522* @return this {@code Group}1523*/1524public Group addComponent(Component component, int min, int pref,1525int max) {1526return addSpring(new ComponentSpring(component, min, pref, max));1527}15281529/**1530* Adds a rigid gap to this {@code Group}.1531*1532* @param size the size of the gap1533* @return this {@code Group}1534* @throws IllegalArgumentException if {@code size} is less than1535* {@code 0}1536*/1537public Group addGap(int size) {1538return addGap(size, size, size);1539}15401541/**1542* Adds a gap to this {@code Group} with the specified size.1543*1544* @param min the minimum size of the gap1545* @param pref the preferred size of the gap1546* @param max the maximum size of the gap1547* @throws IllegalArgumentException if any of the values are1548* less than {@code 0}1549* @return this {@code Group}1550*/1551public Group addGap(int min, int pref, int max) {1552return addSpring(new GapSpring(min, pref, max));1553}15541555Spring getSpring(int index) {1556return springs.get(index);1557}15581559int indexOf(Spring spring) {1560return springs.indexOf(spring);1561}15621563/**1564* Adds the Spring to the list of {@code Spring}s and returns1565* the receiver.1566*/1567Group addSpring(Spring spring) {1568springs.add(spring);1569spring.setParent(this);1570if (!(spring instanceof AutoPreferredGapSpring) ||1571!((AutoPreferredGapSpring)spring).getUserCreated()) {1572springsChanged = true;1573}1574return this;1575}15761577//1578// Spring methods1579//15801581void setSize(int axis, int origin, int size) {1582super.setSize(axis, origin, size);1583if (size == UNSET) {1584for (int counter = springs.size() - 1; counter >= 0;1585counter--) {1586getSpring(counter).setSize(axis, origin, size);1587}1588} else {1589setValidSize(axis, origin, size);1590}1591}15921593/**1594* This is invoked from {@code setSize} if passed a value1595* other than UNSET.1596*/1597abstract void setValidSize(int axis, int origin, int size);15981599int calculateMinimumSize(int axis) {1600return calculateSize(axis, MIN_SIZE);1601}16021603int calculatePreferredSize(int axis) {1604return calculateSize(axis, PREF_SIZE);1605}16061607int calculateMaximumSize(int axis) {1608return calculateSize(axis, MAX_SIZE);1609}16101611/**1612* Calculates the specified size. This is called from1613* one of the {@code getMinimumSize0},1614* {@code getPreferredSize0} or1615* {@code getMaximumSize0} methods. This will invoke1616* to {@code operator} to combine the values.1617*/1618int calculateSize(int axis, int type) {1619int count = springs.size();1620if (count == 0) {1621return 0;1622}1623if (count == 1) {1624return getSpringSize(getSpring(0), axis, type);1625}1626int size = constrain(operator(getSpringSize(getSpring(0), axis,1627type), getSpringSize(getSpring(1), axis, type)));1628for (int counter = 2; counter < count; counter++) {1629size = constrain(operator(size, getSpringSize(1630getSpring(counter), axis, type)));1631}1632return size;1633}16341635int getSpringSize(Spring spring, int axis, int type) {1636switch(type) {1637case MIN_SIZE:1638return spring.getMinimumSize(axis);1639case PREF_SIZE:1640return spring.getPreferredSize(axis);1641case MAX_SIZE:1642return spring.getMaximumSize(axis);1643}1644assert false;1645return 0;1646}16471648/**1649* Used to compute how the two values representing two springs1650* will be combined. For example, a group that layed things out1651* one after the next would return {@code a + b}.1652*/1653abstract int operator(int a, int b);16541655//1656// Padding1657//16581659/**1660* Adjusts the autopadding springs in this group and its children.1661* If {@code insert} is true this will insert auto padding1662* springs, otherwise this will only adjust the springs that1663* comprise auto preferred padding springs.1664*1665* @param axis the axis of the springs; HORIZONTAL or VERTICAL1666* @param leadingPadding List of AutopaddingSprings that occur before1667* this Group1668* @param trailingPadding any trailing autopadding springs are added1669* to this on exit1670* @param leading List of ComponentSprings that occur before this Group1671* @param trailing any trailing ComponentSpring are added to this1672* List1673* @param insert Whether or not to insert AutopaddingSprings or just1674* adjust any existing AutopaddingSprings.1675*/1676abstract void insertAutopadding(int axis,1677List<AutoPreferredGapSpring> leadingPadding,1678List<AutoPreferredGapSpring> trailingPadding,1679List<ComponentSpring> leading, List<ComponentSpring> trailing,1680boolean insert);16811682/**1683* Removes any AutopaddingSprings for this Group and its children.1684*/1685void removeAutopadding() {1686unset();1687for (int counter = springs.size() - 1; counter >= 0; counter--) {1688Spring spring = springs.get(counter);1689if (spring instanceof AutoPreferredGapSpring) {1690if (((AutoPreferredGapSpring)spring).getUserCreated()) {1691((AutoPreferredGapSpring)spring).reset();1692} else {1693springs.remove(counter);1694}1695} else if (spring instanceof Group) {1696((Group)spring).removeAutopadding();1697}1698}1699}17001701void unsetAutopadding() {1702// Clear cached pref/min/max.1703unset();1704for (int counter = springs.size() - 1; counter >= 0; counter--) {1705Spring spring = springs.get(counter);1706if (spring instanceof AutoPreferredGapSpring) {1707spring.unset();1708} else if (spring instanceof Group) {1709((Group)spring).unsetAutopadding();1710}1711}1712}17131714void calculateAutopadding(int axis) {1715for (int counter = springs.size() - 1; counter >= 0; counter--) {1716Spring spring = springs.get(counter);1717if (spring instanceof AutoPreferredGapSpring) {1718// Force size to be reset.1719spring.unset();1720((AutoPreferredGapSpring)spring).calculatePadding(axis);1721} else if (spring instanceof Group) {1722((Group)spring).calculateAutopadding(axis);1723}1724}1725// Clear cached pref/min/max.1726unset();1727}17281729@Override1730boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {1731for (int i = springs.size() - 1; i >= 0; i--) {1732Spring spring = springs.get(i);1733if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {1734return false;1735}1736}1737return true;1738}1739}174017411742/**1743* A {@code Group} that positions and sizes its elements1744* sequentially, one after another. This class has no public1745* constructor, use the {@code createSequentialGroup} method1746* to create one.1747* <p>1748* In order to align a {@code SequentialGroup} along the baseline1749* of a baseline aligned {@code ParallelGroup} you need to specify1750* which of the elements of the {@code SequentialGroup} is used to1751* determine the baseline. The element used to calculate the1752* baseline is specified using one of the {@code add} methods that1753* take a {@code boolean}. The last element added with a value of1754* {@code true} for {@code useAsBaseline} is used to calculate the1755* baseline.1756*1757* @see #createSequentialGroup1758* @since 1.61759*/1760public class SequentialGroup extends Group {1761private Spring baselineSpring;17621763SequentialGroup() {1764}17651766/**1767* {@inheritDoc}1768*/1769public SequentialGroup addGroup(Group group) {1770return (SequentialGroup)super.addGroup(group);1771}17721773/**1774* Adds a {@code Group} to this {@code Group}.1775*1776* @param group the {@code Group} to add1777* @param useAsBaseline whether the specified {@code Group} should1778* be used to calculate the baseline for this {@code Group}1779* @return this {@code Group}1780*/1781public SequentialGroup addGroup(boolean useAsBaseline, Group group) {1782super.addGroup(group);1783if (useAsBaseline) {1784baselineSpring = group;1785}1786return this;1787}17881789/**1790* {@inheritDoc}1791*/1792public SequentialGroup addComponent(Component component) {1793return (SequentialGroup)super.addComponent(component);1794}17951796/**1797* Adds a {@code Component} to this {@code Group}.1798*1799* @param useAsBaseline whether the specified {@code Component} should1800* be used to calculate the baseline for this {@code Group}1801* @param component the {@code Component} to add1802* @return this {@code Group}1803*/1804public SequentialGroup addComponent(boolean useAsBaseline,1805Component component) {1806super.addComponent(component);1807if (useAsBaseline) {1808baselineSpring = springs.get(springs.size() - 1);1809}1810return this;1811}18121813/**1814* {@inheritDoc}1815*/1816public SequentialGroup addComponent(Component component, int min,1817int pref, int max) {1818return (SequentialGroup)super.addComponent(1819component, min, pref, max);1820}18211822/**1823* Adds a {@code Component} to this {@code Group}1824* with the specified size.1825*1826* @param useAsBaseline whether the specified {@code Component} should1827* be used to calculate the baseline for this {@code Group}1828* @param component the {@code Component} to add1829* @param min the minimum size or one of {@code DEFAULT_SIZE} or1830* {@code PREFERRED_SIZE}1831* @param pref the preferred size or one of {@code DEFAULT_SIZE} or1832* {@code PREFERRED_SIZE}1833* @param max the maximum size or one of {@code DEFAULT_SIZE} or1834* {@code PREFERRED_SIZE}1835* @return this {@code Group}1836*/1837public SequentialGroup addComponent(boolean useAsBaseline,1838Component component, int min, int pref, int max) {1839super.addComponent(component, min, pref, max);1840if (useAsBaseline) {1841baselineSpring = springs.get(springs.size() - 1);1842}1843return this;1844}18451846/**1847* {@inheritDoc}1848*/1849public SequentialGroup addGap(int size) {1850return (SequentialGroup)super.addGap(size);1851}18521853/**1854* {@inheritDoc}1855*/1856public SequentialGroup addGap(int min, int pref, int max) {1857return (SequentialGroup)super.addGap(min, pref, max);1858}18591860/**1861* Adds an element representing the preferred gap between two1862* components. The element created to represent the gap is not1863* resizable.1864*1865* @param comp1 the first component1866* @param comp2 the second component1867* @param type the type of gap; one of the constants defined by1868* {@code LayoutStyle}1869* @return this {@code SequentialGroup}1870* @throws IllegalArgumentException if {@code type}, {@code comp1} or1871* {@code comp2} is {@code null}1872* @see LayoutStyle1873*/1874public SequentialGroup addPreferredGap(JComponent comp1,1875JComponent comp2, ComponentPlacement type) {1876return addPreferredGap(comp1, comp2, type, DEFAULT_SIZE,1877PREFERRED_SIZE);1878}18791880/**1881* Adds an element representing the preferred gap between two1882* components.1883*1884* @param comp1 the first component1885* @param comp2 the second component1886* @param type the type of gap1887* @param pref the preferred size of the grap; one of1888* {@code DEFAULT_SIZE} or a value >= 01889* @param max the maximum size of the gap; one of1890* {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}1891* or a value >= 01892* @return this {@code SequentialGroup}1893* @throws IllegalArgumentException if {@code type}, {@code comp1} or1894* {@code comp2} is {@code null}1895* @see LayoutStyle1896*/1897public SequentialGroup addPreferredGap(JComponent comp1,1898JComponent comp2, ComponentPlacement type, int pref,1899int max) {1900if (type == null) {1901throw new IllegalArgumentException("Type must be non-null");1902}1903if (comp1 == null || comp2 == null) {1904throw new IllegalArgumentException(1905"Components must be non-null");1906}1907checkPreferredGapValues(pref, max);1908return (SequentialGroup)addSpring(new PreferredGapSpring(1909comp1, comp2, type, pref, max));1910}19111912/**1913* Adds an element representing the preferred gap between the1914* nearest components. During layout, neighboring1915* components are found, and the size of the added gap is set1916* based on the preferred gap between the components. If no1917* neighboring components are found the gap has a size of {@code 0}.1918* <p>1919* The element created to represent the gap is not1920* resizable.1921*1922* @param type the type of gap; one of1923* {@code LayoutStyle.ComponentPlacement.RELATED} or1924* {@code LayoutStyle.ComponentPlacement.UNRELATED}1925* @return this {@code SequentialGroup}1926* @see LayoutStyle1927* @throws IllegalArgumentException if {@code type} is not one of1928* {@code LayoutStyle.ComponentPlacement.RELATED} or1929* {@code LayoutStyle.ComponentPlacement.UNRELATED}1930*/1931public SequentialGroup addPreferredGap(ComponentPlacement type) {1932return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);1933}19341935/**1936* Adds an element representing the preferred gap between the1937* nearest components. During layout, neighboring1938* components are found, and the minimum of this1939* gap is set based on the size of the preferred gap between the1940* neighboring components. If no neighboring components are found the1941* minimum size is set to 0.1942*1943* @param type the type of gap; one of1944* {@code LayoutStyle.ComponentPlacement.RELATED} or1945* {@code LayoutStyle.ComponentPlacement.UNRELATED}1946* @param pref the preferred size of the grap; one of1947* {@code DEFAULT_SIZE} or a value >= 01948* @param max the maximum size of the gap; one of1949* {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}1950* or a value >= 01951* @return this {@code SequentialGroup}1952* @throws IllegalArgumentException if {@code type} is not one of1953* {@code LayoutStyle.ComponentPlacement.RELATED} or1954* {@code LayoutStyle.ComponentPlacement.UNRELATED}1955* @see LayoutStyle1956*/1957public SequentialGroup addPreferredGap(ComponentPlacement type,1958int pref, int max) {1959if (type != ComponentPlacement.RELATED &&1960type != ComponentPlacement.UNRELATED) {1961throw new IllegalArgumentException(1962"Type must be one of " +1963"LayoutStyle.ComponentPlacement.RELATED or " +1964"LayoutStyle.ComponentPlacement.UNRELATED");1965}1966checkPreferredGapValues(pref, max);1967hasPreferredPaddingSprings = true;1968return (SequentialGroup)addSpring(new AutoPreferredGapSpring(1969type, pref, max));1970}19711972/**1973* Adds an element representing the preferred gap between an edge1974* the container and components that touch the border of the1975* container. This has no effect if the added gap does not1976* touch an edge of the parent container.1977* <p>1978* The element created to represent the gap is not1979* resizable.1980*1981* @return this {@code SequentialGroup}1982*/1983public SequentialGroup addContainerGap() {1984return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);1985}19861987/**1988* Adds an element representing the preferred gap between one1989* edge of the container and the next or previous {@code1990* Component} with the specified size. This has no1991* effect if the next or previous element is not a {@code1992* Component} and does not touch one edge of the parent1993* container.1994*1995* @param pref the preferred size; one of {@code DEFAULT_SIZE} or a1996* value >= 01997* @param max the maximum size; one of {@code DEFAULT_SIZE},1998* {@code PREFERRED_SIZE} or a value >= 01999* @return this {@code SequentialGroup}2000*/2001public SequentialGroup addContainerGap(int pref, int max) {2002if ((pref < 0 && pref != DEFAULT_SIZE) ||2003(max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||2004(pref >= 0 && max >= 0 && pref > max)) {2005throw new IllegalArgumentException(2006"Pref and max must be either DEFAULT_VALUE " +2007"or >= 0 and pref <= max");2008}2009hasPreferredPaddingSprings = true;2010return (SequentialGroup)addSpring(2011new ContainerAutoPreferredGapSpring(pref, max));2012}20132014int operator(int a, int b) {2015return constrain(a) + constrain(b);2016}20172018void setValidSize(int axis, int origin, int size) {2019int pref = getPreferredSize(axis);2020if (size == pref) {2021// Layout at preferred size2022for (Spring spring : springs) {2023int springPref = spring.getPreferredSize(axis);2024spring.setSize(axis, origin, springPref);2025origin += springPref;2026}2027} else if (springs.size() == 1) {2028Spring spring = getSpring(0);2029spring.setSize(axis, origin, Math.min(2030Math.max(size, spring.getMinimumSize(axis)),2031spring.getMaximumSize(axis)));2032} else if (springs.size() > 1) {2033// Adjust between min/pref2034setValidSizeNotPreferred(axis, origin, size);2035}2036}20372038private void setValidSizeNotPreferred(int axis, int origin, int size) {2039int delta = size - getPreferredSize(axis);2040assert delta != 0;2041boolean useMin = (delta < 0);2042int springCount = springs.size();2043if (useMin) {2044delta *= -1;2045}20462047// The following algorithm if used for resizing springs:2048// 1. Calculate the resizability of each spring (pref - min or2049// max - pref) into a list.2050// 2. Sort the list in ascending order2051// 3. Iterate through each of the resizable Springs, attempting2052// to give them (pref - size) / resizeCount2053// 4. For any Springs that can not accommodate that much space2054// add the remainder back to the amount to distribute and2055// recalculate how must space the remaining springs will get.2056// 5. Set the size of the springs.20572058// First pass, sort the resizable springs into the List resizable2059List<SpringDelta> resizable = buildResizableList(axis, useMin);2060int resizableCount = resizable.size();20612062if (resizableCount > 0) {2063// How much we would like to give each Spring.2064int sDelta = delta / resizableCount;2065// Remaining space.2066int slop = delta - sDelta * resizableCount;2067int[] sizes = new int[springCount];2068int sign = useMin ? -1 : 1;2069// Second pass, accumulate the resulting deltas (relative to2070// preferred) into sizes.2071for (int counter = 0; counter < resizableCount; counter++) {2072SpringDelta springDelta = resizable.get(counter);2073if ((counter + 1) == resizableCount) {2074sDelta += slop;2075}2076springDelta.delta = Math.min(sDelta, springDelta.delta);2077delta -= springDelta.delta;2078if (springDelta.delta != sDelta && counter + 1 <2079resizableCount) {2080// Spring didn't take all the space, reset how much2081// each spring will get.2082sDelta = delta / (resizableCount - counter - 1);2083slop = delta - sDelta * (resizableCount - counter - 1);2084}2085sizes[springDelta.index] = sign * springDelta.delta;2086}20872088// And finally set the size of each spring2089for (int counter = 0; counter < springCount; counter++) {2090Spring spring = getSpring(counter);2091int sSize = spring.getPreferredSize(axis) + sizes[counter];2092spring.setSize(axis, origin, sSize);2093origin += sSize;2094}2095} else {2096// Nothing resizable, use the min or max of each of the2097// springs.2098for (int counter = 0; counter < springCount; counter++) {2099Spring spring = getSpring(counter);2100int sSize;2101if (useMin) {2102sSize = spring.getMinimumSize(axis);2103} else {2104sSize = spring.getMaximumSize(axis);2105}2106spring.setSize(axis, origin, sSize);2107origin += sSize;2108}2109}2110}21112112/**2113* Returns the sorted list of SpringDelta's for the current set of2114* Springs. The list is ordered based on the amount of flexibility of2115* the springs.2116*/2117private List<SpringDelta> buildResizableList(int axis,2118boolean useMin) {2119// First pass, figure out what is resizable2120int size = springs.size();2121List<SpringDelta> sorted = new ArrayList<SpringDelta>(size);2122for (int counter = 0; counter < size; counter++) {2123Spring spring = getSpring(counter);2124int sDelta;2125if (useMin) {2126sDelta = spring.getPreferredSize(axis) -2127spring.getMinimumSize(axis);2128} else {2129sDelta = spring.getMaximumSize(axis) -2130spring.getPreferredSize(axis);2131}2132if (sDelta > 0) {2133sorted.add(new SpringDelta(counter, sDelta));2134}2135}2136Collections.sort(sorted);2137return sorted;2138}21392140private int indexOfNextNonZeroSpring(2141int index, boolean treatAutopaddingAsZeroSized) {2142while (index < springs.size()) {2143Spring spring = springs.get(index);2144if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {2145return index;2146}2147index++;2148}2149return index;2150}21512152@Override2153void insertAutopadding(int axis,2154List<AutoPreferredGapSpring> leadingPadding,2155List<AutoPreferredGapSpring> trailingPadding,2156List<ComponentSpring> leading, List<ComponentSpring> trailing,2157boolean insert) {2158List<AutoPreferredGapSpring> newLeadingPadding =2159new ArrayList<AutoPreferredGapSpring>(leadingPadding);2160List<AutoPreferredGapSpring> newTrailingPadding =2161new ArrayList<AutoPreferredGapSpring>(1);2162List<ComponentSpring> newLeading =2163new ArrayList<ComponentSpring>(leading);2164List<ComponentSpring> newTrailing = null;2165int counter = 0;2166// Warning, this must use springs.size, as it may change during the2167// loop.2168while (counter < springs.size()) {2169Spring spring = getSpring(counter);2170if (spring instanceof AutoPreferredGapSpring) {2171if (newLeadingPadding.size() == 0) {2172// Autopadding spring. Set the sources of the2173// autopadding spring based on newLeading.2174AutoPreferredGapSpring padding =2175(AutoPreferredGapSpring)spring;2176padding.setSources(newLeading);2177newLeading.clear();2178counter = indexOfNextNonZeroSpring(counter + 1, true);2179if (counter == springs.size()) {2180// Last spring in the list, add it to2181// trailingPadding.2182if (!(padding instanceof2183ContainerAutoPreferredGapSpring)) {2184trailingPadding.add(padding);2185}2186} else {2187newLeadingPadding.clear();2188newLeadingPadding.add(padding);2189}2190} else {2191counter = indexOfNextNonZeroSpring(counter + 1, true);2192}2193} else {2194// Not a padding spring2195if (newLeading.size() > 0 && newLeadingPadding.isEmpty() && insert) {2196// There's leading ComponentSprings, create an2197// autopadding spring.2198AutoPreferredGapSpring padding =2199new AutoPreferredGapSpring();2200// Force the newly created spring to be considered2201// by NOT incrementing counter2202springs.add(counter, padding);2203continue;2204}2205if (spring instanceof ComponentSpring) {2206// Spring is a Component, make it the target of any2207// leading AutopaddingSpring.2208ComponentSpring cSpring = (ComponentSpring)spring;2209if (!cSpring.isVisible()) {2210counter++;2211continue;2212}2213for (AutoPreferredGapSpring gapSpring : newLeadingPadding) {2214gapSpring.addTarget(cSpring, axis);2215}2216newLeading.clear();2217newLeadingPadding.clear();2218counter = indexOfNextNonZeroSpring(counter + 1, false);2219if (counter == springs.size()) {2220// Last Spring, add it to trailing2221trailing.add(cSpring);2222} else {2223// Not that last Spring, add it to leading2224newLeading.add(cSpring);2225}2226} else if (spring instanceof Group) {2227// Forward call to child Group2228if (newTrailing == null) {2229newTrailing = new ArrayList<ComponentSpring>(1);2230} else {2231newTrailing.clear();2232}2233newTrailingPadding.clear();2234((Group)spring).insertAutopadding(axis,2235newLeadingPadding, newTrailingPadding,2236newLeading, newTrailing, insert);2237newLeading.clear();2238newLeadingPadding.clear();2239counter = indexOfNextNonZeroSpring(2240counter + 1, (newTrailing.size() == 0));2241if (counter == springs.size()) {2242trailing.addAll(newTrailing);2243trailingPadding.addAll(newTrailingPadding);2244} else {2245newLeading.addAll(newTrailing);2246newLeadingPadding.addAll(newTrailingPadding);2247}2248} else {2249// Gap2250newLeadingPadding.clear();2251newLeading.clear();2252counter++;2253}2254}2255}2256}22572258int getBaseline() {2259if (baselineSpring != null) {2260int baseline = baselineSpring.getBaseline();2261if (baseline >= 0) {2262int size = 0;2263for (Spring spring : springs) {2264if (spring == baselineSpring) {2265return size + baseline;2266} else {2267size += spring.getPreferredSize(VERTICAL);2268}2269}2270}2271}2272return -1;2273}22742275BaselineResizeBehavior getBaselineResizeBehavior() {2276if (isResizable(VERTICAL)) {2277if (!baselineSpring.isResizable(VERTICAL)) {2278// Spring to use for baseline isn't resizable. In this case2279// baseline resize behavior can be determined based on how2280// preceding springs resize.2281boolean leadingResizable = false;2282for (Spring spring : springs) {2283if (spring == baselineSpring) {2284break;2285} else if (spring.isResizable(VERTICAL)) {2286leadingResizable = true;2287break;2288}2289}2290boolean trailingResizable = false;2291for (int i = springs.size() - 1; i >= 0; i--) {2292Spring spring = springs.get(i);2293if (spring == baselineSpring) {2294break;2295}2296if (spring.isResizable(VERTICAL)) {2297trailingResizable = true;2298break;2299}2300}2301if (leadingResizable && !trailingResizable) {2302return BaselineResizeBehavior.CONSTANT_DESCENT;2303} else if (!leadingResizable && trailingResizable) {2304return BaselineResizeBehavior.CONSTANT_ASCENT;2305}2306// If we get here, both leading and trailing springs are2307// resizable. Fall through to OTHER.2308} else {2309BaselineResizeBehavior brb = baselineSpring.getBaselineResizeBehavior();2310if (brb == BaselineResizeBehavior.CONSTANT_ASCENT) {2311for (Spring spring : springs) {2312if (spring == baselineSpring) {2313return BaselineResizeBehavior.CONSTANT_ASCENT;2314}2315if (spring.isResizable(VERTICAL)) {2316return BaselineResizeBehavior.OTHER;2317}2318}2319} else if (brb == BaselineResizeBehavior.CONSTANT_DESCENT) {2320for (int i = springs.size() - 1; i >= 0; i--) {2321Spring spring = springs.get(i);2322if (spring == baselineSpring) {2323return BaselineResizeBehavior.CONSTANT_DESCENT;2324}2325if (spring.isResizable(VERTICAL)) {2326return BaselineResizeBehavior.OTHER;2327}2328}2329}2330}2331return BaselineResizeBehavior.OTHER;2332}2333// Not resizable, treat as constant_ascent2334return BaselineResizeBehavior.CONSTANT_ASCENT;2335}23362337private void checkPreferredGapValues(int pref, int max) {2338if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) ||2339(max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||2340(pref >= 0 && max >= 0 && pref > max)) {2341throw new IllegalArgumentException(2342"Pref and max must be either DEFAULT_SIZE, " +2343"PREFERRED_SIZE, or >= 0 and pref <= max");2344}2345}2346}234723482349/**2350* Used by SequentialGroup in calculating resizability of springs.2351*/2352private static final class SpringDelta implements Comparable<SpringDelta> {2353// Original index.2354public final int index;2355// Delta, one of pref - min or max - pref.2356public int delta;23572358public SpringDelta(int index, int delta) {2359this.index = index;2360this.delta = delta;2361}23622363public int compareTo(SpringDelta o) {2364return delta - o.delta;2365}23662367public String toString() {2368return super.toString() + "[index=" + index + ", delta=" +2369delta + "]";2370}2371}237223732374/**2375* A {@code Group} that aligns and sizes its children.2376* {@code ParallelGroup} aligns its children in2377* four possible ways: along the baseline, centered, anchored to the2378* leading edge, or anchored to the trailing edge.2379* <h2>Baseline</h2>2380* A {@code ParallelGroup} that aligns its children along the2381* baseline must first decide where the baseline is2382* anchored. The baseline can either be anchored to the top, or2383* anchored to the bottom of the group. That is, the distance between the2384* baseline and the beginning of the group can be a constant2385* distance, or the distance between the end of the group and the2386* baseline can be a constant distance. The possible choices2387* correspond to the {@code BaselineResizeBehavior} constants2388* {@link2389* java.awt.Component.BaselineResizeBehavior#CONSTANT_ASCENT CONSTANT_ASCENT} and2390* {@link2391* java.awt.Component.BaselineResizeBehavior#CONSTANT_DESCENT CONSTANT_DESCENT}.2392* <p>2393* The baseline anchor may be explicitly specified by the2394* {@code createBaselineGroup} method, or determined based on the elements.2395* If not explicitly specified, the baseline will be anchored to2396* the bottom if all the elements with a baseline, and that are2397* aligned to the baseline, have a baseline resize behavior of2398* {@code CONSTANT_DESCENT}; otherwise the baseline is anchored to the top2399* of the group.2400* <p>2401* Elements aligned to the baseline are resizable if they have2402* a baseline resize behavior of {@code CONSTANT_ASCENT} or2403* {@code CONSTANT_DESCENT}. Elements with a baseline resize2404* behavior of {@code OTHER} or {@code CENTER_OFFSET} are not resizable.2405* <p>2406* The baseline is calculated based on the preferred height of each2407* of the elements that have a baseline. The baseline is2408* calculated using the following algorithm:2409* {@code max(maxNonBaselineHeight, maxAscent + maxDescent)}, where the2410* {@code maxNonBaselineHeight} is the maximum height of all elements2411* that do not have a baseline, or are not aligned along the baseline.2412* {@code maxAscent} is the maximum ascent (baseline) of all elements that2413* have a baseline and are aligned along the baseline.2414* {@code maxDescent} is the maximum descent (preferred height - baseline)2415* of all elements that have a baseline and are aligned along the baseline.2416* <p>2417* A {@code ParallelGroup} that aligns its elements along the baseline2418* is only useful along the vertical axis. If you create a2419* baseline group and use it along the horizontal axis an2420* {@code IllegalStateException} is thrown when you ask2421* {@code GroupLayout} for the minimum, preferred or maximum size or2422* attempt to layout the components.2423* <p>2424* Elements that are not aligned to the baseline and smaller than the size2425* of the {@code ParallelGroup} are positioned in one of three2426* ways: centered, anchored to the leading edge, or anchored to the2427* trailing edge.2428*2429* <h2>Non-baseline {@code ParallelGroup}</h2>2430* {@code ParallelGroup}s created with an alignment other than2431* {@code BASELINE} align elements that are smaller than the size2432* of the group in one of three ways: centered, anchored to the2433* leading edge, or anchored to the trailing edge.2434* <p>2435* The leading edge is based on the axis and {@code2436* ComponentOrientation}. For the vertical axis the top edge is2437* always the leading edge, and the bottom edge is always the2438* trailing edge. When the {@code ComponentOrientation} is {@code2439* LEFT_TO_RIGHT}, the leading edge is the left edge and the2440* trailing edge the right edge. A {@code ComponentOrientation} of2441* {@code RIGHT_TO_LEFT} flips the left and right edges. Child2442* elements are aligned based on the specified alignment the2443* element was added with. If you do not specify an alignment, the2444* alignment specified for the {@code ParallelGroup} is used.2445* <p>2446* To align elements along the baseline you {@code createBaselineGroup},2447* or {@code createParallelGroup} with an alignment of {@code BASELINE}.2448* If the group was not created with a baseline alignment, and you attempt2449* to add an element specifying a baseline alignment, an2450* {@code IllegalArgumentException} is thrown.2451*2452* @see #createParallelGroup()2453* @see #createBaselineGroup(boolean,boolean)2454* @since 1.62455*/2456public class ParallelGroup extends Group {2457// How children are layed out.2458private final Alignment childAlignment;2459// Whether or not we're resizable.2460private final boolean resizable;24612462ParallelGroup(Alignment childAlignment, boolean resizable) {2463this.childAlignment = childAlignment;2464this.resizable = resizable;2465}24662467/**2468* {@inheritDoc}2469*/2470public ParallelGroup addGroup(Group group) {2471return (ParallelGroup)super.addGroup(group);2472}24732474/**2475* {@inheritDoc}2476*/2477public ParallelGroup addComponent(Component component) {2478return (ParallelGroup)super.addComponent(component);2479}24802481/**2482* {@inheritDoc}2483*/2484public ParallelGroup addComponent(Component component, int min, int pref,2485int max) {2486return (ParallelGroup)super.addComponent(component, min, pref, max);2487}24882489/**2490* {@inheritDoc}2491*/2492public ParallelGroup addGap(int pref) {2493return (ParallelGroup)super.addGap(pref);2494}24952496/**2497* {@inheritDoc}2498*/2499public ParallelGroup addGap(int min, int pref, int max) {2500return (ParallelGroup)super.addGap(min, pref, max);2501}25022503/**2504* Adds a {@code Group} to this {@code ParallelGroup} with the2505* specified alignment. If the child is smaller than the2506* {@code Group} it is aligned based on the specified2507* alignment.2508*2509* @param alignment the alignment2510* @param group the {@code Group} to add2511* @return this {@code ParallelGroup}2512* @throws IllegalArgumentException if {@code alignment} is2513* {@code null}2514*/2515public ParallelGroup addGroup(Alignment alignment, Group group) {2516checkChildAlignment(alignment);2517group.setAlignment(alignment);2518return (ParallelGroup)addSpring(group);2519}25202521/**2522* Adds a {@code Component} to this {@code ParallelGroup} with2523* the specified alignment.2524*2525* @param alignment the alignment2526* @param component the {@code Component} to add2527* @return this {@code Group}2528* @throws IllegalArgumentException if {@code alignment} is2529* {@code null}2530*/2531public ParallelGroup addComponent(Component component,2532Alignment alignment) {2533return addComponent(component, alignment, DEFAULT_SIZE, DEFAULT_SIZE,2534DEFAULT_SIZE);2535}25362537/**2538* Adds a {@code Component} to this {@code ParallelGroup} with the2539* specified alignment and size.2540*2541* @param alignment the alignment2542* @param component the {@code Component} to add2543* @param min the minimum size2544* @param pref the preferred size2545* @param max the maximum size2546* @throws IllegalArgumentException if {@code alignment} is2547* {@code null}2548* @return this {@code Group}2549*/2550public ParallelGroup addComponent(Component component,2551Alignment alignment, int min, int pref, int max) {2552checkChildAlignment(alignment);2553ComponentSpring spring = new ComponentSpring(component,2554min, pref, max);2555spring.setAlignment(alignment);2556return (ParallelGroup)addSpring(spring);2557}25582559boolean isResizable() {2560return resizable;2561}25622563int operator(int a, int b) {2564return Math.max(a, b);2565}25662567int calculateMinimumSize(int axis) {2568if (!isResizable()) {2569return getPreferredSize(axis);2570}2571return super.calculateMinimumSize(axis);2572}25732574int calculateMaximumSize(int axis) {2575if (!isResizable()) {2576return getPreferredSize(axis);2577}2578return super.calculateMaximumSize(axis);2579}25802581void setValidSize(int axis, int origin, int size) {2582for (Spring spring : springs) {2583setChildSize(spring, axis, origin, size);2584}2585}25862587void setChildSize(Spring spring, int axis, int origin, int size) {2588Alignment alignment = spring.getAlignment();2589int springSize = Math.min(2590Math.max(spring.getMinimumSize(axis), size),2591spring.getMaximumSize(axis));2592if (alignment == null) {2593alignment = childAlignment;2594}2595switch (alignment) {2596case TRAILING:2597spring.setSize(axis, origin + size - springSize,2598springSize);2599break;2600case CENTER:2601spring.setSize(axis, origin +2602(size - springSize) / 2,springSize);2603break;2604default: // LEADING, or BASELINE2605spring.setSize(axis, origin, springSize);2606break;2607}2608}26092610@Override2611void insertAutopadding(int axis,2612List<AutoPreferredGapSpring> leadingPadding,2613List<AutoPreferredGapSpring> trailingPadding,2614List<ComponentSpring> leading, List<ComponentSpring> trailing,2615boolean insert) {2616for (Spring spring : springs) {2617if (spring instanceof ComponentSpring) {2618if (((ComponentSpring)spring).isVisible()) {2619for (AutoPreferredGapSpring gapSpring :2620leadingPadding) {2621gapSpring.addTarget((ComponentSpring)spring, axis);2622}2623trailing.add((ComponentSpring)spring);2624}2625} else if (spring instanceof Group) {2626((Group)spring).insertAutopadding(axis, leadingPadding,2627trailingPadding, leading, trailing, insert);2628} else if (spring instanceof AutoPreferredGapSpring) {2629((AutoPreferredGapSpring)spring).setSources(leading);2630trailingPadding.add((AutoPreferredGapSpring)spring);2631}2632}2633}26342635private void checkChildAlignment(Alignment alignment) {2636checkChildAlignment(alignment, (this instanceof BaselineGroup));2637}26382639private void checkChildAlignment(Alignment alignment,2640boolean allowsBaseline) {2641if (alignment == null) {2642throw new IllegalArgumentException("Alignment must be non-null");2643}2644if (!allowsBaseline && alignment == Alignment.BASELINE) {2645throw new IllegalArgumentException("Alignment must be one of:" +2646"LEADING, TRAILING or CENTER");2647}2648}2649}265026512652/**2653* An extension of {@code ParallelGroup} that aligns its2654* constituent {@code Spring}s along the baseline.2655*/2656private class BaselineGroup extends ParallelGroup {2657// Whether or not all child springs have a baseline2658private boolean allSpringsHaveBaseline;26592660// max(spring.getBaseline()) of all springs aligned along the baseline2661// that have a baseline2662private int prefAscent;26632664// max(spring.getPreferredSize().height - spring.getBaseline()) of all2665// springs aligned along the baseline that have a baseline2666private int prefDescent;26672668// Whether baselineAnchoredToTop was explicitly set2669private boolean baselineAnchorSet;26702671// Whether the baseline is anchored to the top or the bottom.2672// If anchored to the top the baseline is always at prefAscent,2673// otherwise the baseline is at (height - prefDescent)2674private boolean baselineAnchoredToTop;26752676// Whether or not the baseline has been calculated.2677private boolean calcedBaseline;26782679BaselineGroup(boolean resizable) {2680super(Alignment.LEADING, resizable);2681prefAscent = prefDescent = -1;2682calcedBaseline = false;2683}26842685BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) {2686this(resizable);2687this.baselineAnchoredToTop = baselineAnchoredToTop;2688baselineAnchorSet = true;2689}26902691void unset() {2692super.unset();2693prefAscent = prefDescent = -1;2694calcedBaseline = false;2695}26962697void setValidSize(int axis, int origin, int size) {2698checkAxis(axis);2699if (prefAscent == -1) {2700super.setValidSize(axis, origin, size);2701} else {2702// do baseline layout2703baselineLayout(origin, size);2704}2705}27062707int calculateSize(int axis, int type) {2708checkAxis(axis);2709if (!calcedBaseline) {2710calculateBaselineAndResizeBehavior();2711}2712if (type == MIN_SIZE) {2713return calculateMinSize();2714}2715if (type == MAX_SIZE) {2716return calculateMaxSize();2717}2718if (allSpringsHaveBaseline) {2719return prefAscent + prefDescent;2720}2721return Math.max(prefAscent + prefDescent,2722super.calculateSize(axis, type));2723}27242725private void calculateBaselineAndResizeBehavior() {2726// calculate baseline2727prefAscent = 0;2728prefDescent = 0;2729int baselineSpringCount = 0;2730BaselineResizeBehavior resizeBehavior = null;2731for (Spring spring : springs) {2732if (spring.getAlignment() == null ||2733spring.getAlignment() == Alignment.BASELINE) {2734int baseline = spring.getBaseline();2735if (baseline >= 0) {2736if (spring.isResizable(VERTICAL)) {2737BaselineResizeBehavior brb = spring.2738getBaselineResizeBehavior();2739if (resizeBehavior == null) {2740resizeBehavior = brb;2741} else if (brb != resizeBehavior) {2742resizeBehavior = BaselineResizeBehavior.2743CONSTANT_ASCENT;2744}2745}2746prefAscent = Math.max(prefAscent, baseline);2747prefDescent = Math.max(prefDescent, spring.2748getPreferredSize(VERTICAL) - baseline);2749baselineSpringCount++;2750}2751}2752}2753if (!baselineAnchorSet) {2754if (resizeBehavior == BaselineResizeBehavior.CONSTANT_DESCENT){2755this.baselineAnchoredToTop = false;2756} else {2757this.baselineAnchoredToTop = true;2758}2759}2760allSpringsHaveBaseline = (baselineSpringCount == springs.size());2761calcedBaseline = true;2762}27632764private int calculateMaxSize() {2765int maxAscent = prefAscent;2766int maxDescent = prefDescent;2767int nonBaselineMax = 0;2768for (Spring spring : springs) {2769int baseline;2770int springMax = spring.getMaximumSize(VERTICAL);2771if ((spring.getAlignment() == null ||2772spring.getAlignment() == Alignment.BASELINE) &&2773(baseline = spring.getBaseline()) >= 0) {2774int springPref = spring.getPreferredSize(VERTICAL);2775if (springPref != springMax) {2776switch (spring.getBaselineResizeBehavior()) {2777case CONSTANT_ASCENT:2778if (baselineAnchoredToTop) {2779maxDescent = Math.max(maxDescent,2780springMax - baseline);2781}2782break;2783case CONSTANT_DESCENT:2784if (!baselineAnchoredToTop) {2785maxAscent = Math.max(maxAscent,2786springMax - springPref + baseline);2787}2788break;2789default: // CENTER_OFFSET and OTHER, not resizable2790break;2791}2792}2793} else {2794// Not aligned along the baseline, or no baseline.2795nonBaselineMax = Math.max(nonBaselineMax, springMax);2796}2797}2798return Math.max(nonBaselineMax, maxAscent + maxDescent);2799}28002801private int calculateMinSize() {2802int minAscent = 0;2803int minDescent = 0;2804int nonBaselineMin = 0;2805if (baselineAnchoredToTop) {2806minAscent = prefAscent;2807} else {2808minDescent = prefDescent;2809}2810for (Spring spring : springs) {2811int springMin = spring.getMinimumSize(VERTICAL);2812int baseline;2813if ((spring.getAlignment() == null ||2814spring.getAlignment() == Alignment.BASELINE) &&2815(baseline = spring.getBaseline()) >= 0) {2816int springPref = spring.getPreferredSize(VERTICAL);2817BaselineResizeBehavior brb = spring.2818getBaselineResizeBehavior();2819switch (brb) {2820case CONSTANT_ASCENT:2821if (baselineAnchoredToTop) {2822minDescent = Math.max(springMin - baseline,2823minDescent);2824} else {2825minAscent = Math.max(baseline, minAscent);2826}2827break;2828case CONSTANT_DESCENT:2829if (!baselineAnchoredToTop) {2830minAscent = Math.max(2831baseline - (springPref - springMin),2832minAscent);2833} else {2834minDescent = Math.max(springPref - baseline,2835minDescent);2836}2837break;2838default:2839// CENTER_OFFSET and OTHER are !resizable, use2840// the preferred size.2841minAscent = Math.max(baseline, minAscent);2842minDescent = Math.max(springPref - baseline,2843minDescent);2844break;2845}2846} else {2847// Not aligned along the baseline, or no baseline.2848nonBaselineMin = Math.max(nonBaselineMin, springMin);2849}2850}2851return Math.max(nonBaselineMin, minAscent + minDescent);2852}28532854/**2855* Lays out springs that have a baseline along the baseline. All2856* others are centered.2857*/2858private void baselineLayout(int origin, int size) {2859int ascent;2860int descent;2861if (baselineAnchoredToTop) {2862ascent = prefAscent;2863descent = size - ascent;2864} else {2865ascent = size - prefDescent;2866descent = prefDescent;2867}2868for (Spring spring : springs) {2869Alignment alignment = spring.getAlignment();2870if (alignment == null || alignment == Alignment.BASELINE) {2871int baseline = spring.getBaseline();2872if (baseline >= 0) {2873int springMax = spring.getMaximumSize(VERTICAL);2874int springPref = spring.getPreferredSize(VERTICAL);2875int height = springPref;2876int y;2877switch(spring.getBaselineResizeBehavior()) {2878case CONSTANT_ASCENT:2879y = origin + ascent - baseline;2880height = Math.min(descent, springMax -2881baseline) + baseline;2882break;2883case CONSTANT_DESCENT:2884height = Math.min(ascent, springMax -2885springPref + baseline) +2886(springPref - baseline);2887y = origin + ascent +2888(springPref - baseline) - height;2889break;2890default: // CENTER_OFFSET & OTHER, not resizable2891y = origin + ascent - baseline;2892break;2893}2894spring.setSize(VERTICAL, y, height);2895} else {2896setChildSize(spring, VERTICAL, origin, size);2897}2898} else {2899setChildSize(spring, VERTICAL, origin, size);2900}2901}2902}29032904int getBaseline() {2905if (springs.size() > 1) {2906// Force the baseline to be calculated2907getPreferredSize(VERTICAL);2908return prefAscent;2909} else if (springs.size() == 1) {2910return springs.get(0).getBaseline();2911}2912return -1;2913}29142915BaselineResizeBehavior getBaselineResizeBehavior() {2916if (springs.size() == 1) {2917return springs.get(0).getBaselineResizeBehavior();2918}2919if (baselineAnchoredToTop) {2920return BaselineResizeBehavior.CONSTANT_ASCENT;2921}2922return BaselineResizeBehavior.CONSTANT_DESCENT;2923}29242925// If the axis is VERTICAL, throws an IllegalStateException2926private void checkAxis(int axis) {2927if (axis == HORIZONTAL) {2928throw new IllegalStateException(2929"Baseline must be used along vertical axis");2930}2931}2932}293329342935private final class ComponentSpring extends Spring {2936private Component component;2937private int origin;29382939// min/pref/max are either a value >= 0 or one of2940// DEFAULT_SIZE or PREFERRED_SIZE2941private final int min;2942private final int pref;2943private final int max;29442945// Baseline for the component, computed as necessary.2946private int baseline = -1;29472948// Whether or not the size has been requested yet.2949private boolean installed;29502951private ComponentSpring(Component component, int min, int pref,2952int max) {2953this.component = component;2954if (component == null) {2955throw new IllegalArgumentException(2956"Component must be non-null");2957}29582959checkSize(min, pref, max, true);29602961this.min = min;2962this.max = max;2963this.pref = pref;29642965// getComponentInfo makes sure component is a child of the2966// Container GroupLayout is the LayoutManager for.2967getComponentInfo(component);2968}29692970int calculateMinimumSize(int axis) {2971if (isLinked(axis)) {2972return getLinkSize(axis, MIN_SIZE);2973}2974return calculateNonlinkedMinimumSize(axis);2975}29762977int calculatePreferredSize(int axis) {2978if (isLinked(axis)) {2979return getLinkSize(axis, PREF_SIZE);2980}2981int min = getMinimumSize(axis);2982int pref = calculateNonlinkedPreferredSize(axis);2983int max = getMaximumSize(axis);2984return Math.min(max, Math.max(min, pref));2985}29862987int calculateMaximumSize(int axis) {2988if (isLinked(axis)) {2989return getLinkSize(axis, MAX_SIZE);2990}2991return Math.max(getMinimumSize(axis),2992calculateNonlinkedMaximumSize(axis));2993}29942995boolean isVisible() {2996return getComponentInfo(getComponent()).isVisible();2997}29982999int calculateNonlinkedMinimumSize(int axis) {3000if (!isVisible()) {3001return 0;3002}3003if (min >= 0) {3004return min;3005}3006if (min == PREFERRED_SIZE) {3007return calculateNonlinkedPreferredSize(axis);3008}3009assert (min == DEFAULT_SIZE);3010return getSizeAlongAxis(axis, component.getMinimumSize());3011}30123013int calculateNonlinkedPreferredSize(int axis) {3014if (!isVisible()) {3015return 0;3016}3017if (pref >= 0) {3018return pref;3019}3020assert (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE);3021return getSizeAlongAxis(axis, component.getPreferredSize());3022}30233024int calculateNonlinkedMaximumSize(int axis) {3025if (!isVisible()) {3026return 0;3027}3028if (max >= 0) {3029return max;3030}3031if (max == PREFERRED_SIZE) {3032return calculateNonlinkedPreferredSize(axis);3033}3034assert (max == DEFAULT_SIZE);3035return getSizeAlongAxis(axis, component.getMaximumSize());3036}30373038private int getSizeAlongAxis(int axis, Dimension size) {3039return (axis == HORIZONTAL) ? size.width : size.height;3040}30413042private int getLinkSize(int axis, int type) {3043if (!isVisible()) {3044return 0;3045}3046ComponentInfo ci = getComponentInfo(component);3047return ci.getLinkSize(axis, type);3048}30493050void setSize(int axis, int origin, int size) {3051super.setSize(axis, origin, size);3052this.origin = origin;3053if (size == UNSET) {3054baseline = -1;3055}3056}30573058int getOrigin() {3059return origin;3060}30613062void setComponent(Component component) {3063this.component = component;3064}30653066Component getComponent() {3067return component;3068}30693070int getBaseline() {3071if (baseline == -1) {3072Spring horizontalSpring = getComponentInfo(component).3073horizontalSpring;3074int width = horizontalSpring.getPreferredSize(HORIZONTAL);3075int height = getPreferredSize(VERTICAL);3076if (width > 0 && height > 0) {3077baseline = component.getBaseline(width, height);3078}3079}3080return baseline;3081}30823083BaselineResizeBehavior getBaselineResizeBehavior() {3084return getComponent().getBaselineResizeBehavior();3085}30863087private boolean isLinked(int axis) {3088return getComponentInfo(component).isLinked(axis);3089}30903091void installIfNecessary(int axis) {3092if (!installed) {3093installed = true;3094if (axis == HORIZONTAL) {3095getComponentInfo(component).horizontalSpring = this;3096} else {3097getComponentInfo(component).verticalSpring = this;3098}3099}3100}31013102@Override3103boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3104return !isVisible();3105}3106}310731083109/**3110* Spring representing the preferred distance between two components.3111*/3112private class PreferredGapSpring extends Spring {3113private final JComponent source;3114private final JComponent target;3115private final ComponentPlacement type;3116private final int pref;3117private final int max;31183119PreferredGapSpring(JComponent source, JComponent target,3120ComponentPlacement type, int pref, int max) {3121this.source = source;3122this.target = target;3123this.type = type;3124this.pref = pref;3125this.max = max;3126}31273128int calculateMinimumSize(int axis) {3129return getPadding(axis);3130}31313132int calculatePreferredSize(int axis) {3133if (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE) {3134return getMinimumSize(axis);3135}3136int min = getMinimumSize(axis);3137int max = getMaximumSize(axis);3138return Math.min(max, Math.max(min, pref));3139}31403141int calculateMaximumSize(int axis) {3142if (max == PREFERRED_SIZE || max == DEFAULT_SIZE) {3143return getPadding(axis);3144}3145return Math.max(getMinimumSize(axis), max);3146}31473148private int getPadding(int axis) {3149int position;3150if (axis == HORIZONTAL) {3151position = SwingConstants.EAST;3152} else {3153position = SwingConstants.SOUTH;3154}3155return getLayoutStyle0().getPreferredGap(source,3156target, type, position, host);3157}31583159@Override3160boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3161return false;3162}3163}316431653166/**3167* Spring represented a certain amount of space.3168*/3169private class GapSpring extends Spring {3170private final int min;3171private final int pref;3172private final int max;31733174GapSpring(int min, int pref, int max) {3175checkSize(min, pref, max, false);3176this.min = min;3177this.pref = pref;3178this.max = max;3179}31803181int calculateMinimumSize(int axis) {3182if (min == PREFERRED_SIZE) {3183return getPreferredSize(axis);3184}3185return min;3186}31873188int calculatePreferredSize(int axis) {3189return pref;3190}31913192int calculateMaximumSize(int axis) {3193if (max == PREFERRED_SIZE) {3194return getPreferredSize(axis);3195}3196return max;3197}31983199@Override3200boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3201return false;3202}3203}320432053206/**3207* Spring reprensenting the distance between any number of sources and3208* targets. The targets and sources are computed during layout. An3209* instance of this can either be dynamically created when3210* autocreatePadding is true, or explicitly created by the developer.3211*/3212private class AutoPreferredGapSpring extends Spring {3213List<ComponentSpring> sources;3214ComponentSpring source;3215private List<AutoPreferredGapMatch> matches;3216int size;3217int lastSize;3218private final int pref;3219private final int max;3220// Type of gap3221private ComponentPlacement type;3222private boolean userCreated;32233224private AutoPreferredGapSpring() {3225this.pref = PREFERRED_SIZE;3226this.max = PREFERRED_SIZE;3227this.type = ComponentPlacement.RELATED;3228}32293230AutoPreferredGapSpring(int pref, int max) {3231this.pref = pref;3232this.max = max;3233}32343235AutoPreferredGapSpring(ComponentPlacement type, int pref, int max) {3236this.type = type;3237this.pref = pref;3238this.max = max;3239this.userCreated = true;3240}32413242public void setSource(ComponentSpring source) {3243this.source = source;3244}32453246public void setSources(List<ComponentSpring> sources) {3247this.sources = new ArrayList<ComponentSpring>(sources);3248}32493250public void setUserCreated(boolean userCreated) {3251this.userCreated = userCreated;3252}32533254public boolean getUserCreated() {3255return userCreated;3256}32573258void unset() {3259lastSize = getSize();3260super.unset();3261size = 0;3262}32633264public void reset() {3265size = 0;3266sources = null;3267source = null;3268matches = null;3269}32703271public void calculatePadding(int axis) {3272size = UNSET;3273int maxPadding = UNSET;3274if (matches != null) {3275LayoutStyle p = getLayoutStyle0();3276int position;3277if (axis == HORIZONTAL) {3278if (isLeftToRight()) {3279position = SwingConstants.EAST;3280} else {3281position = SwingConstants.WEST;3282}3283} else {3284position = SwingConstants.SOUTH;3285}3286for (int i = matches.size() - 1; i >= 0; i--) {3287AutoPreferredGapMatch match = matches.get(i);3288maxPadding = Math.max(maxPadding,3289calculatePadding(p, position, match.source,3290match.target));3291}3292}3293if (size == UNSET) {3294size = 0;3295}3296if (maxPadding == UNSET) {3297maxPadding = 0;3298}3299if (lastSize != UNSET) {3300size += Math.min(maxPadding, lastSize);3301}3302}33033304private int calculatePadding(LayoutStyle p, int position,3305ComponentSpring source,3306ComponentSpring target) {3307int delta = target.getOrigin() - (source.getOrigin() +3308source.getSize());3309if (delta >= 0) {3310int padding;3311if ((source.getComponent() instanceof JComponent) &&3312(target.getComponent() instanceof JComponent)) {3313padding = p.getPreferredGap(3314(JComponent)source.getComponent(),3315(JComponent)target.getComponent(), type, position,3316host);3317} else {3318padding = 10;3319}3320if (padding > delta) {3321size = Math.max(size, padding - delta);3322}3323return padding;3324}3325return 0;3326}33273328public void addTarget(ComponentSpring spring, int axis) {3329int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL;3330if (source != null) {3331if (areParallelSiblings(source.getComponent(),3332spring.getComponent(), oAxis)) {3333addValidTarget(source, spring);3334}3335} else {3336Component component = spring.getComponent();3337for (int counter = sources.size() - 1; counter >= 0;3338counter--){3339ComponentSpring source = sources.get(counter);3340if (areParallelSiblings(source.getComponent(),3341component, oAxis)) {3342addValidTarget(source, spring);3343}3344}3345}3346}33473348private void addValidTarget(ComponentSpring source,3349ComponentSpring target) {3350if (matches == null) {3351matches = new ArrayList<AutoPreferredGapMatch>(1);3352}3353matches.add(new AutoPreferredGapMatch(source, target));3354}33553356int calculateMinimumSize(int axis) {3357return size;3358}33593360int calculatePreferredSize(int axis) {3361if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) {3362return size;3363}3364return Math.max(size, pref);3365}33663367int calculateMaximumSize(int axis) {3368if (max >= 0) {3369return Math.max(getPreferredSize(axis), max);3370}3371return size;3372}33733374String getMatchDescription() {3375return (matches == null) ? "" : matches.toString();3376}33773378public String toString() {3379return super.toString() + getMatchDescription();3380}33813382@Override3383boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3384return treatAutopaddingAsZeroSized;3385}3386}338733883389/**3390* Represents two springs that should have autopadding inserted between3391* them.3392*/3393private static final class AutoPreferredGapMatch {3394public final ComponentSpring source;3395public final ComponentSpring target;33963397AutoPreferredGapMatch(ComponentSpring source, ComponentSpring target) {3398this.source = source;3399this.target = target;3400}34013402private String toString(ComponentSpring spring) {3403return spring.getComponent().getName();3404}34053406public String toString() {3407return "[" + toString(source) + "-" + toString(target) + "]";3408}3409}341034113412/**3413* An extension of AutopaddingSpring used for container level padding.3414*/3415private class ContainerAutoPreferredGapSpring extends3416AutoPreferredGapSpring {3417private List<ComponentSpring> targets;34183419ContainerAutoPreferredGapSpring() {3420super();3421setUserCreated(true);3422}34233424ContainerAutoPreferredGapSpring(int pref, int max) {3425super(pref, max);3426setUserCreated(true);3427}34283429public void addTarget(ComponentSpring spring, int axis) {3430if (targets == null) {3431targets = new ArrayList<ComponentSpring>(1);3432}3433targets.add(spring);3434}34353436public void calculatePadding(int axis) {3437LayoutStyle p = getLayoutStyle0();3438int maxPadding = 0;3439int position;3440size = 0;3441if (targets != null) {3442// Leading3443if (axis == HORIZONTAL) {3444if (isLeftToRight()) {3445position = SwingConstants.WEST;3446} else {3447position = SwingConstants.EAST;3448}3449} else {3450position = SwingConstants.SOUTH;3451}3452for (int i = targets.size() - 1; i >= 0; i--) {3453ComponentSpring targetSpring = targets.get(i);3454int padding = 10;3455if (targetSpring.getComponent() instanceof JComponent) {3456padding = p.getContainerGap(3457(JComponent)targetSpring.getComponent(),3458position, host);3459maxPadding = Math.max(padding, maxPadding);3460padding -= targetSpring.getOrigin();3461} else {3462maxPadding = Math.max(padding, maxPadding);3463}3464size = Math.max(size, padding);3465}3466} else {3467// Trailing3468if (axis == HORIZONTAL) {3469if (isLeftToRight()) {3470position = SwingConstants.EAST;3471} else {3472position = SwingConstants.WEST;3473}3474} else {3475position = SwingConstants.SOUTH;3476}3477if (sources != null) {3478for (int i = sources.size() - 1; i >= 0; i--) {3479ComponentSpring sourceSpring = sources.get(i);3480maxPadding = Math.max(maxPadding,3481updateSize(p, sourceSpring, position));3482}3483} else if (source != null) {3484maxPadding = updateSize(p, source, position);3485}3486}3487if (lastSize != UNSET) {3488size += Math.min(maxPadding, lastSize);3489}3490}34913492private int updateSize(LayoutStyle p, ComponentSpring sourceSpring,3493int position) {3494int padding = 10;3495if (sourceSpring.getComponent() instanceof JComponent) {3496padding = p.getContainerGap(3497(JComponent)sourceSpring.getComponent(), position,3498host);3499}3500int delta = Math.max(0, getParent().getSize() -3501sourceSpring.getSize() - sourceSpring.getOrigin());3502size = Math.max(size, padding - delta);3503return padding;3504}35053506String getMatchDescription() {3507if (targets != null) {3508return "leading: " + targets.toString();3509}3510if (sources != null) {3511return "trailing: " + sources.toString();3512}3513return "--";3514}3515}351635173518// LinkInfo contains the set of ComponentInfosthat are linked along a3519// particular axis.3520private static class LinkInfo {3521private final int axis;3522private final List<ComponentInfo> linked;3523private int size;35243525LinkInfo(int axis) {3526linked = new ArrayList<ComponentInfo>();3527size = UNSET;3528this.axis = axis;3529}35303531public void add(ComponentInfo child) {3532LinkInfo childMaster = child.getLinkInfo(axis, false);3533if (childMaster == null) {3534linked.add(child);3535child.setLinkInfo(axis, this);3536} else if (childMaster != this) {3537linked.addAll(childMaster.linked);3538for (ComponentInfo childInfo : childMaster.linked) {3539childInfo.setLinkInfo(axis, this);3540}3541}3542clearCachedSize();3543}35443545public void remove(ComponentInfo info) {3546linked.remove(info);3547info.setLinkInfo(axis, null);3548if (linked.size() == 1) {3549linked.get(0).setLinkInfo(axis, null);3550}3551clearCachedSize();3552}35533554public void clearCachedSize() {3555size = UNSET;3556}35573558public int getSize(int axis) {3559if (size == UNSET) {3560size = calculateLinkedSize(axis);3561}3562return size;3563}35643565private int calculateLinkedSize(int axis) {3566int size = 0;3567for (ComponentInfo info : linked) {3568ComponentSpring spring;3569if (axis == HORIZONTAL) {3570spring = info.horizontalSpring;3571} else {3572assert (axis == VERTICAL);3573spring = info.verticalSpring;3574}3575size = Math.max(size,3576spring.calculateNonlinkedPreferredSize(axis));3577}3578return size;3579}3580}35813582/**3583* Tracks the horizontal/vertical Springs for a Component.3584* This class is also used to handle Springs that have their sizes3585* linked.3586*/3587private class ComponentInfo {3588// Component being layed out3589private Component component;35903591ComponentSpring horizontalSpring;3592ComponentSpring verticalSpring;35933594// If the component's size is linked to other components, the3595// horizontalMaster and/or verticalMaster reference the group of3596// linked components.3597private LinkInfo horizontalMaster;3598private LinkInfo verticalMaster;35993600private boolean visible;3601private Boolean honorsVisibility;36023603ComponentInfo(Component component) {3604this.component = component;3605updateVisibility();3606}36073608public void dispose() {3609// Remove horizontal/vertical springs3610removeSpring(horizontalSpring);3611horizontalSpring = null;3612removeSpring(verticalSpring);3613verticalSpring = null;3614// Clean up links3615if (horizontalMaster != null) {3616horizontalMaster.remove(this);3617}3618if (verticalMaster != null) {3619verticalMaster.remove(this);3620}3621}36223623void setHonorsVisibility(Boolean honorsVisibility) {3624this.honorsVisibility = honorsVisibility;3625}36263627private void removeSpring(Spring spring) {3628if (spring != null) {3629((Group)spring.getParent()).springs.remove(spring);3630}3631}36323633public boolean isVisible() {3634return visible;3635}36363637/**3638* Updates the cached visibility.3639*3640* @return true if the visibility changed3641*/3642boolean updateVisibility() {3643boolean honorsVisibility;3644if (this.honorsVisibility == null) {3645honorsVisibility = GroupLayout.this.getHonorsVisibility();3646} else {3647honorsVisibility = this.honorsVisibility;3648}3649boolean newVisible = (honorsVisibility) ?3650component.isVisible() : true;3651if (visible != newVisible) {3652visible = newVisible;3653return true;3654}3655return false;3656}36573658public void setBounds(Insets insets, int parentWidth, boolean ltr) {3659int x = horizontalSpring.getOrigin();3660int w = horizontalSpring.getSize();3661int y = verticalSpring.getOrigin();3662int h = verticalSpring.getSize();36633664if (!ltr) {3665x = parentWidth - x - w;3666}3667component.setBounds(x + insets.left, y + insets.top, w, h);3668}36693670public void setComponent(Component component) {3671this.component = component;3672if (horizontalSpring != null) {3673horizontalSpring.setComponent(component);3674}3675if (verticalSpring != null) {3676verticalSpring.setComponent(component);3677}3678}36793680public Component getComponent() {3681return component;3682}36833684/**3685* Returns true if this component has its size linked to3686* other components.3687*/3688public boolean isLinked(int axis) {3689if (axis == HORIZONTAL) {3690return horizontalMaster != null;3691}3692assert (axis == VERTICAL);3693return (verticalMaster != null);3694}36953696private void setLinkInfo(int axis, LinkInfo linkInfo) {3697if (axis == HORIZONTAL) {3698horizontalMaster = linkInfo;3699} else {3700assert (axis == VERTICAL);3701verticalMaster = linkInfo;3702}3703}37043705public LinkInfo getLinkInfo(int axis) {3706return getLinkInfo(axis, true);3707}37083709private LinkInfo getLinkInfo(int axis, boolean create) {3710if (axis == HORIZONTAL) {3711if (horizontalMaster == null && create) {3712// horizontalMaster field is directly set by adding3713// us to the LinkInfo.3714new LinkInfo(HORIZONTAL).add(this);3715}3716return horizontalMaster;3717} else {3718assert (axis == VERTICAL);3719if (verticalMaster == null && create) {3720// verticalMaster field is directly set by adding3721// us to the LinkInfo.3722new LinkInfo(VERTICAL).add(this);3723}3724return verticalMaster;3725}3726}37273728public void clearCachedSize() {3729if (horizontalMaster != null) {3730horizontalMaster.clearCachedSize();3731}3732if (verticalMaster != null) {3733verticalMaster.clearCachedSize();3734}3735}37363737int getLinkSize(int axis, int type) {3738if (axis == HORIZONTAL) {3739return horizontalMaster.getSize(axis);3740} else {3741assert (axis == VERTICAL);3742return verticalMaster.getSize(axis);3743}3744}37453746}3747}374837493750