Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/swing/GroupLayout.java
38829 views
/*1* Copyright (c) 2006, 2013, 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/**221* Indicates the size from the component or gap should be used for a222* particular range value.223*224* @see Group225*/226public static final int DEFAULT_SIZE = -1;227228/**229* Indicates the preferred size from the component or gap should230* be used for a particular range value.231*232* @see Group233*/234public static final int PREFERRED_SIZE = -2;235236// Whether or not we automatically try and create the preferred237// padding between components.238private boolean autocreatePadding;239240// Whether or not we automatically try and create the preferred241// padding between components the touch the edge of the container and242// the container.243private boolean autocreateContainerPadding;244245/**246* Group responsible for layout along the horizontal axis. This is NOT247* the user specified group, use getHorizontalGroup to dig that out.248*/249private Group horizontalGroup;250251/**252* Group responsible for layout along the vertical axis. This is NOT253* the user specified group, use getVerticalGroup to dig that out.254*/255private Group verticalGroup;256257// Maps from Component to ComponentInfo. This is used for tracking258// information specific to a Component.259private Map<Component,ComponentInfo> componentInfos;260261// Container we're doing layout for.262private Container host;263264// Used by areParallelSiblings, cached to avoid excessive garbage.265private Set<Spring> tmpParallelSet;266267// Indicates Springs have changed in some way since last change.268private boolean springsChanged;269270// Indicates invalidateLayout has been invoked.271private boolean isValid;272273// Whether or not any preferred padding (or container padding) springs274// exist275private boolean hasPreferredPaddingSprings;276277/**278* The LayoutStyle instance to use, if null the sharedInstance is used.279*/280private LayoutStyle layoutStyle;281282/**283* If true, components that are not visible are treated as though they284* aren't there.285*/286private boolean honorsVisibility;287288289/**290* Enumeration of the possible ways {@code ParallelGroup} can align291* its children.292*293* @see #createParallelGroup(Alignment)294* @since 1.6295*/296public enum Alignment {297/**298* Indicates the elements should be299* aligned to the origin. For the horizontal axis with a left to300* right orientation this means aligned to the left edge. For the301* vertical axis leading means aligned to the top edge.302*303* @see #createParallelGroup(Alignment)304*/305LEADING,306307/**308* Indicates the elements should be aligned to the end of the309* region. For the horizontal axis with a left to right310* orientation this means aligned to the right edge. For the311* vertical axis trailing means aligned to the bottom edge.312*313* @see #createParallelGroup(Alignment)314*/315TRAILING,316317/**318* Indicates the elements should be centered in319* the region.320*321* @see #createParallelGroup(Alignment)322*/323CENTER,324325/**326* Indicates the elements should be aligned along327* their baseline.328*329* @see #createParallelGroup(Alignment)330* @see #createBaselineGroup(boolean,boolean)331*/332BASELINE333}334335336private static void checkSize(int min, int pref, int max,337boolean isComponentSpring) {338checkResizeType(min, isComponentSpring);339if (!isComponentSpring && pref < 0) {340throw new IllegalArgumentException("Pref must be >= 0");341} else if (isComponentSpring) {342checkResizeType(pref, true);343}344checkResizeType(max, isComponentSpring);345checkLessThan(min, pref);346checkLessThan(pref, max);347}348349private static void checkResizeType(int type, boolean isComponentSpring) {350if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE &&351type != PREFERRED_SIZE) ||352(!isComponentSpring && type != PREFERRED_SIZE))) {353throw new IllegalArgumentException("Invalid size");354}355}356357private static void checkLessThan(int min, int max) {358if (min >= 0 && max >= 0 && min > max) {359throw new IllegalArgumentException(360"Following is not met: min<=pref<=max");361}362}363364/**365* Creates a {@code GroupLayout} for the specified {@code Container}.366*367* @param host the {@code Container} the {@code GroupLayout} is368* the {@code LayoutManager} for369* @throws IllegalArgumentException if host is {@code null}370*/371public GroupLayout(Container host) {372if (host == null) {373throw new IllegalArgumentException("Container must be non-null");374}375honorsVisibility = true;376this.host = host;377setHorizontalGroup(createParallelGroup(Alignment.LEADING, true));378setVerticalGroup(createParallelGroup(Alignment.LEADING, true));379componentInfos = new HashMap<Component,ComponentInfo>();380tmpParallelSet = new HashSet<Spring>();381}382383/**384* Sets whether component visibility is considered when sizing and385* positioning components. A value of {@code true} indicates that386* non-visible components should not be treated as part of the387* layout. A value of {@code false} indicates that components should be388* positioned and sized regardless of visibility.389* <p>390* A value of {@code false} is useful when the visibility of components391* is dynamically adjusted and you don't want surrounding components and392* the sizing to change.393* <p>394* The specified value is used for components that do not have an395* explicit visibility specified.396* <p>397* The default is {@code true}.398*399* @param honorsVisibility whether component visibility is considered when400* sizing and positioning components401* @see #setHonorsVisibility(Component,Boolean)402*/403public void setHonorsVisibility(boolean honorsVisibility) {404if (this.honorsVisibility != honorsVisibility) {405this.honorsVisibility = honorsVisibility;406springsChanged = true;407isValid = false;408invalidateHost();409}410}411412/**413* Returns whether component visibility is considered when sizing and414* positioning components.415*416* @return whether component visibility is considered when sizing and417* positioning components418*/419public boolean getHonorsVisibility() {420return honorsVisibility;421}422423/**424* Sets whether the component's visibility is considered for425* sizing and positioning. A value of {@code Boolean.TRUE}426* indicates that if {@code component} is not visible it should427* not be treated as part of the layout. A value of {@code false}428* indicates that {@code component} is positioned and sized429* regardless of it's visibility. A value of {@code null}430* indicates the value specified by the single argument method {@code431* setHonorsVisibility} should be used.432* <p>433* If {@code component} is not a child of the {@code Container} this434* {@code GroupLayout} is managing, it will be added to the435* {@code Container}.436*437* @param component the component438* @param honorsVisibility whether visibility of this {@code component} should be439* considered for sizing and positioning440* @throws IllegalArgumentException if {@code component} is {@code null}441* @see #setHonorsVisibility(Component,Boolean)442*/443public void setHonorsVisibility(Component component,444Boolean honorsVisibility) {445if (component == null) {446throw new IllegalArgumentException("Component must be non-null");447}448getComponentInfo(component).setHonorsVisibility(honorsVisibility);449springsChanged = true;450isValid = false;451invalidateHost();452}453454/**455* Sets whether a gap between components should automatically be456* created. For example, if this is {@code true} and you add two457* components to a {@code SequentialGroup} a gap between the458* two components is automatically be created. The default is459* {@code false}.460*461* @param autoCreatePadding whether a gap between components is462* automatically created463*/464public void setAutoCreateGaps(boolean autoCreatePadding) {465if (this.autocreatePadding != autoCreatePadding) {466this.autocreatePadding = autoCreatePadding;467invalidateHost();468}469}470471/**472* Returns {@code true} if gaps between components are automatically473* created.474*475* @return {@code true} if gaps between components are automatically476* created477*/478public boolean getAutoCreateGaps() {479return autocreatePadding;480}481482/**483* Sets whether a gap between the container and components that484* touch the border of the container should automatically be485* created. The default is {@code false}.486*487* @param autoCreateContainerPadding whether a gap between the container and488* components that touch the border of the container should489* automatically be created490*/491public void setAutoCreateContainerGaps(boolean autoCreateContainerPadding){492if (this.autocreateContainerPadding != autoCreateContainerPadding) {493this.autocreateContainerPadding = autoCreateContainerPadding;494horizontalGroup = createTopLevelGroup(getHorizontalGroup());495verticalGroup = createTopLevelGroup(getVerticalGroup());496invalidateHost();497}498}499500/**501* Returns {@code true} if gaps between the container and components that502* border the container are automatically created.503*504* @return {@code true} if gaps between the container and components that505* border the container are automatically created506*/507public boolean getAutoCreateContainerGaps() {508return autocreateContainerPadding;509}510511/**512* Sets the {@code Group} that positions and sizes513* components along the horizontal axis.514*515* @param group the {@code Group} that positions and sizes516* components along the horizontal axis517* @throws IllegalArgumentException if group is {@code null}518*/519public void setHorizontalGroup(Group group) {520if (group == null) {521throw new IllegalArgumentException("Group must be non-null");522}523horizontalGroup = createTopLevelGroup(group);524invalidateHost();525}526527/**528* Returns the {@code Group} that positions and sizes components529* along the horizontal axis.530*531* @return the {@code Group} responsible for positioning and532* sizing component along the horizontal axis533*/534private Group getHorizontalGroup() {535int index = 0;536if (horizontalGroup.springs.size() > 1) {537index = 1;538}539return (Group)horizontalGroup.springs.get(index);540}541542/**543* Sets the {@code Group} that positions and sizes544* components along the vertical axis.545*546* @param group the {@code Group} that positions and sizes547* components along the vertical axis548* @throws IllegalArgumentException if group is {@code null}549*/550public void setVerticalGroup(Group group) {551if (group == null) {552throw new IllegalArgumentException("Group must be non-null");553}554verticalGroup = createTopLevelGroup(group);555invalidateHost();556}557558/**559* Returns the {@code Group} that positions and sizes components560* along the vertical axis.561*562* @return the {@code Group} responsible for positioning and563* sizing component along the vertical axis564*/565private Group getVerticalGroup() {566int index = 0;567if (verticalGroup.springs.size() > 1) {568index = 1;569}570return (Group)verticalGroup.springs.get(index);571}572573/**574* Wraps the user specified group in a sequential group. If575* container gaps should be generated the necessary springs are576* added.577*/578private Group createTopLevelGroup(Group specifiedGroup) {579SequentialGroup group = createSequentialGroup();580if (getAutoCreateContainerGaps()) {581group.addSpring(new ContainerAutoPreferredGapSpring());582group.addGroup(specifiedGroup);583group.addSpring(new ContainerAutoPreferredGapSpring());584} else {585group.addGroup(specifiedGroup);586}587return group;588}589590/**591* Creates and returns a {@code SequentialGroup}.592*593* @return a new {@code SequentialGroup}594*/595public SequentialGroup createSequentialGroup() {596return new SequentialGroup();597}598599/**600* Creates and returns a {@code ParallelGroup} with an alignment of601* {@code Alignment.LEADING}. This is a cover method for the more602* general {@code createParallelGroup(Alignment)} method.603*604* @return a new {@code ParallelGroup}605* @see #createParallelGroup(Alignment)606*/607public ParallelGroup createParallelGroup() {608return createParallelGroup(Alignment.LEADING);609}610611/**612* Creates and returns a {@code ParallelGroup} with the specified613* alignment. This is a cover method for the more general {@code614* createParallelGroup(Alignment,boolean)} method with {@code true}615* supplied for the second argument.616*617* @param alignment the alignment for the elements of the group618* @throws IllegalArgumentException if {@code alignment} is {@code null}619* @return a new {@code ParallelGroup}620* @see #createBaselineGroup621* @see ParallelGroup622*/623public ParallelGroup createParallelGroup(Alignment alignment) {624return createParallelGroup(alignment, true);625}626627/**628* Creates and returns a {@code ParallelGroup} with the specified629* alignment and resize behavior. The {@code630* alignment} argument specifies how children elements are631* positioned that do not fill the group. For example, if a {@code632* ParallelGroup} with an alignment of {@code TRAILING} is given633* 100 and a child only needs 50, the child is634* positioned at the position 50 (with a component orientation of635* left-to-right).636* <p>637* Baseline alignment is only useful when used along the vertical638* axis. A {@code ParallelGroup} created with a baseline alignment639* along the horizontal axis is treated as {@code LEADING}.640* <p>641* Refer to {@link GroupLayout.ParallelGroup ParallelGroup} for details on642* the behavior of baseline groups.643*644* @param alignment the alignment for the elements of the group645* @param resizable {@code true} if the group is resizable; if the group646* is not resizable the preferred size is used for the647* minimum and maximum size of the group648* @throws IllegalArgumentException if {@code alignment} is {@code null}649* @return a new {@code ParallelGroup}650* @see #createBaselineGroup651* @see GroupLayout.ParallelGroup652*/653public ParallelGroup createParallelGroup(Alignment alignment,654boolean resizable){655if (alignment == null) {656throw new IllegalArgumentException("alignment must be non null");657}658659if (alignment == Alignment.BASELINE) {660return new BaselineGroup(resizable);661}662return new ParallelGroup(alignment, resizable);663}664665/**666* Creates and returns a {@code ParallelGroup} that aligns it's667* elements along the baseline.668*669* @param resizable whether the group is resizable670* @param anchorBaselineToTop whether the baseline is anchored to671* the top or bottom of the group672* @see #createBaselineGroup673* @see ParallelGroup674*/675public ParallelGroup createBaselineGroup(boolean resizable,676boolean anchorBaselineToTop) {677return new BaselineGroup(resizable, anchorBaselineToTop);678}679680/**681* Forces the specified components to have the same size682* regardless of their preferred, minimum or maximum sizes. Components that683* are linked are given the maximum of the preferred size of each of684* the linked components. For example, if you link two components with685* a preferred width of 10 and 20, both components are given a width of 20.686* <p>687* This can be used multiple times to force any number of688* components to share the same size.689* <p>690* Linked Components are not be resizable.691*692* @param components the {@code Component}s that are to have the same size693* @throws IllegalArgumentException if {@code components} is694* {@code null}, or contains {@code null}695* @see #linkSize(int,Component[])696*/697public void linkSize(Component... components) {698linkSize(SwingConstants.HORIZONTAL, components);699linkSize(SwingConstants.VERTICAL, components);700}701702/**703* Forces the specified components to have the same size along the704* specified axis regardless of their preferred, minimum or705* maximum sizes. Components that are linked are given the maximum706* of the preferred size of each of the linked components. For707* example, if you link two components along the horizontal axis708* and the preferred width is 10 and 20, both components are given709* a width of 20.710* <p>711* This can be used multiple times to force any number of712* components to share the same size.713* <p>714* Linked {@code Component}s are not be resizable.715*716* @param components the {@code Component}s that are to have the same size717* @param axis the axis to link the size along; one of718* {@code SwingConstants.HORIZONTAL} or719* {@code SwingConstans.VERTICAL}720* @throws IllegalArgumentException if {@code components} is721* {@code null}, or contains {@code null}; or {@code axis}722* is not {@code SwingConstants.HORIZONTAL} or723* {@code SwingConstants.VERTICAL}724*/725public void linkSize(int axis, Component... components) {726if (components == null) {727throw new IllegalArgumentException("Components must be non-null");728}729for (int counter = components.length - 1; counter >= 0; counter--) {730Component c = components[counter];731if (components[counter] == null) {732throw new IllegalArgumentException(733"Components must be non-null");734}735// Force the component to be added736getComponentInfo(c);737}738int glAxis;739if (axis == SwingConstants.HORIZONTAL) {740glAxis = HORIZONTAL;741} else if (axis == SwingConstants.VERTICAL) {742glAxis = VERTICAL;743} else {744throw new IllegalArgumentException("Axis must be one of " +745"SwingConstants.HORIZONTAL or SwingConstants.VERTICAL");746}747LinkInfo master = getComponentInfo(748components[components.length - 1]).getLinkInfo(glAxis);749for (int counter = components.length - 2; counter >= 0; counter--) {750master.add(getComponentInfo(components[counter]));751}752invalidateHost();753}754755/**756* Replaces an existing component with a new one.757*758* @param existingComponent the component that should be removed759* and replaced with {@code newComponent}760* @param newComponent the component to put in761* {@code existingComponent}'s place762* @throws IllegalArgumentException if either of the components are763* {@code null} or {@code existingComponent} is not being managed764* by this layout manager765*/766public void replace(Component existingComponent, Component newComponent) {767if (existingComponent == null || newComponent == null) {768throw new IllegalArgumentException("Components must be non-null");769}770// Make sure all the components have been registered, otherwise we may771// not update the correct Springs.772if (springsChanged) {773registerComponents(horizontalGroup, HORIZONTAL);774registerComponents(verticalGroup, VERTICAL);775}776ComponentInfo info = componentInfos.remove(existingComponent);777if (info == null) {778throw new IllegalArgumentException("Component must already exist");779}780host.remove(existingComponent);781if (newComponent.getParent() != host) {782host.add(newComponent);783}784info.setComponent(newComponent);785componentInfos.put(newComponent, info);786invalidateHost();787}788789/**790* Sets the {@code LayoutStyle} used to calculate the preferred791* gaps between components. A value of {@code null} indicates the792* shared instance of {@code LayoutStyle} should be used.793*794* @param layoutStyle the {@code LayoutStyle} to use795* @see LayoutStyle796*/797public void setLayoutStyle(LayoutStyle layoutStyle) {798this.layoutStyle = layoutStyle;799invalidateHost();800}801802/**803* Returns the {@code LayoutStyle} used for calculating the preferred804* gap between components. This returns the value specified to805* {@code setLayoutStyle}, which may be {@code null}.806*807* @return the {@code LayoutStyle} used for calculating the preferred808* gap between components809*/810public LayoutStyle getLayoutStyle() {811return layoutStyle;812}813814private LayoutStyle getLayoutStyle0() {815LayoutStyle layoutStyle = getLayoutStyle();816if (layoutStyle == null) {817layoutStyle = LayoutStyle.getInstance();818}819return layoutStyle;820}821822private void invalidateHost() {823if (host instanceof JComponent) {824((JComponent)host).revalidate();825} else {826host.invalidate();827}828host.repaint();829}830831//832// LayoutManager833//834/**835* Notification that a {@code Component} has been added to836* the parent container. You should not invoke this method837* directly, instead you should use one of the {@code Group}838* methods to add a {@code Component}.839*840* @param name the string to be associated with the component841* @param component the {@code Component} to be added842*/843public void addLayoutComponent(String name, Component component) {844}845846/**847* Notification that a {@code Component} has been removed from848* the parent container. You should not invoke this method849* directly, instead invoke {@code remove} on the parent850* {@code Container}.851*852* @param component the component to be removed853* @see java.awt.Component#remove854*/855public void removeLayoutComponent(Component component) {856ComponentInfo info = componentInfos.remove(component);857if (info != null) {858info.dispose();859springsChanged = true;860isValid = false;861}862}863864/**865* Returns the preferred size for the specified container.866*867* @param parent the container to return the preferred size for868* @return the preferred size for {@code parent}869* @throws IllegalArgumentException if {@code parent} is not870* the same {@code Container} this was created with871* @throws IllegalStateException if any of the components added to872* this layout are not in both a horizontal and vertical group873* @see java.awt.Container#getPreferredSize874*/875public Dimension preferredLayoutSize(Container parent) {876checkParent(parent);877prepare(PREF_SIZE);878return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),879verticalGroup.getPreferredSize(VERTICAL));880}881882/**883* Returns the minimum size for the specified container.884*885* @param parent the container to return the size for886* @return the minimum size for {@code parent}887* @throws IllegalArgumentException if {@code parent} is not888* the same {@code Container} that this was created with889* @throws IllegalStateException if any of the components added to890* this layout are not in both a horizontal and vertical group891* @see java.awt.Container#getMinimumSize892*/893public Dimension minimumLayoutSize(Container parent) {894checkParent(parent);895prepare(MIN_SIZE);896return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),897verticalGroup.getMinimumSize(VERTICAL));898}899900/**901* Lays out the specified container.902*903* @param parent the container to be laid out904* @throws IllegalStateException if any of the components added to905* this layout are not in both a horizontal and vertical group906*/907public void layoutContainer(Container parent) {908// Step 1: Prepare for layout.909prepare(SPECIFIC_SIZE);910Insets insets = parent.getInsets();911int width = parent.getWidth() - insets.left - insets.right;912int height = parent.getHeight() - insets.top - insets.bottom;913boolean ltr = isLeftToRight();914if (getAutoCreateGaps() || getAutoCreateContainerGaps() ||915hasPreferredPaddingSprings) {916// Step 2: Calculate autopadding springs917calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0,918width);919calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0,920height);921}922// Step 3: set the size of the groups.923horizontalGroup.setSize(HORIZONTAL, 0, width);924verticalGroup.setSize(VERTICAL, 0, height);925// Step 4: apply the size to the components.926for (ComponentInfo info : componentInfos.values()) {927info.setBounds(insets, width, ltr);928}929}930931//932// LayoutManager2933//934/**935* Notification that a {@code Component} has been added to936* the parent container. You should not invoke this method937* directly, instead you should use one of the {@code Group}938* methods to add a {@code Component}.939*940* @param component the component added941* @param constraints description of where to place the component942*/943public void addLayoutComponent(Component component, Object constraints) {944}945946/**947* Returns the maximum size for the specified container.948*949* @param parent the container to return the size for950* @return the maximum size for {@code parent}951* @throws IllegalArgumentException if {@code parent} is not952* the same {@code Container} that this was created with953* @throws IllegalStateException if any of the components added to954* this layout are not in both a horizontal and vertical group955* @see java.awt.Container#getMaximumSize956*/957public Dimension maximumLayoutSize(Container parent) {958checkParent(parent);959prepare(MAX_SIZE);960return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),961verticalGroup.getMaximumSize(VERTICAL));962}963964/**965* Returns the alignment along the x axis. This specifies how966* the component would like to be aligned relative to other967* components. The value should be a number between 0 and 1968* where 0 represents alignment along the origin, 1 is aligned969* the furthest away from the origin, 0.5 is centered, etc.970*971* @param parent the {@code Container} hosting this {@code LayoutManager}972* @throws IllegalArgumentException if {@code parent} is not973* the same {@code Container} that this was created with974* @return the alignment; this implementation returns {@code .5}975*/976public float getLayoutAlignmentX(Container parent) {977checkParent(parent);978return .5f;979}980981/**982* Returns the alignment along the y axis. This specifies how983* the component would like to be aligned relative to other984* components. The value should be a number between 0 and 1985* where 0 represents alignment along the origin, 1 is aligned986* the furthest away from the origin, 0.5 is centered, etc.987*988* @param parent the {@code Container} hosting this {@code LayoutManager}989* @throws IllegalArgumentException if {@code parent} is not990* the same {@code Container} that this was created with991* @return alignment; this implementation returns {@code .5}992*/993public float getLayoutAlignmentY(Container parent) {994checkParent(parent);995return .5f;996}997998/**999* Invalidates the layout, indicating that if the layout manager1000* has cached information it should be discarded.1001*1002* @param parent the {@code Container} hosting this LayoutManager1003* @throws IllegalArgumentException if {@code parent} is not1004* the same {@code Container} that this was created with1005*/1006public void invalidateLayout(Container parent) {1007checkParent(parent);1008// invalidateLayout is called from Container.invalidate, which1009// does NOT grab the treelock. All other methods do. To make sure1010// there aren't any possible threading problems we grab the tree lock1011// here.1012synchronized(parent.getTreeLock()) {1013isValid = false;1014}1015}10161017private void prepare(int sizeType) {1018boolean visChanged = false;1019// Step 1: If not-valid, clear springs and update visibility.1020if (!isValid) {1021isValid = true;1022horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);1023verticalGroup.setSize(VERTICAL, UNSET, UNSET);1024for (ComponentInfo ci : componentInfos.values()) {1025if (ci.updateVisibility()) {1026visChanged = true;1027}1028ci.clearCachedSize();1029}1030}1031// Step 2: Make sure components are bound to ComponentInfos1032if (springsChanged) {1033registerComponents(horizontalGroup, HORIZONTAL);1034registerComponents(verticalGroup, VERTICAL);1035}1036// Step 3: Adjust the autopadding. This removes existing1037// autopadding, then recalculates where it should go.1038if (springsChanged || visChanged) {1039checkComponents();1040horizontalGroup.removeAutopadding();1041verticalGroup.removeAutopadding();1042if (getAutoCreateGaps()) {1043insertAutopadding(true);1044} else if (hasPreferredPaddingSprings ||1045getAutoCreateContainerGaps()) {1046insertAutopadding(false);1047}1048springsChanged = false;1049}1050// Step 4: (for min/pref/max size calculations only) calculate the1051// autopadding. This invokes for unsetting the calculated values, then1052// recalculating them.1053// If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this1054// step will be done later on.1055if (sizeType != SPECIFIC_SIZE && (getAutoCreateGaps() ||1056getAutoCreateContainerGaps() || hasPreferredPaddingSprings)) {1057calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0);1058calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);1059}1060}10611062private void calculateAutopadding(Group group, int axis, int sizeType,1063int origin, int size) {1064group.unsetAutopadding();1065switch(sizeType) {1066case MIN_SIZE:1067size = group.getMinimumSize(axis);1068break;1069case PREF_SIZE:1070size = group.getPreferredSize(axis);1071break;1072case MAX_SIZE:1073size = group.getMaximumSize(axis);1074break;1075default:1076break;1077}1078group.setSize(axis, origin, size);1079group.calculateAutopadding(axis);1080}10811082private void checkComponents() {1083for (ComponentInfo info : componentInfos.values()) {1084if (info.horizontalSpring == null) {1085throw new IllegalStateException(info.component +1086" is not attached to a horizontal group");1087}1088if (info.verticalSpring == null) {1089throw new IllegalStateException(info.component +1090" is not attached to a vertical group");1091}1092}1093}10941095private void registerComponents(Group group, int axis) {1096List<Spring> springs = group.springs;1097for (int counter = springs.size() - 1; counter >= 0; counter--) {1098Spring spring = springs.get(counter);1099if (spring instanceof ComponentSpring) {1100((ComponentSpring)spring).installIfNecessary(axis);1101} else if (spring instanceof Group) {1102registerComponents((Group)spring, axis);1103}1104}1105}11061107private Dimension adjustSize(int width, int height) {1108Insets insets = host.getInsets();1109return new Dimension(width + insets.left + insets.right,1110height + insets.top + insets.bottom);1111}11121113private void checkParent(Container parent) {1114if (parent != host) {1115throw new IllegalArgumentException(1116"GroupLayout can only be used with one Container at a time");1117}1118}11191120/**1121* Returns the {@code ComponentInfo} for the specified Component,1122* creating one if necessary.1123*/1124private ComponentInfo getComponentInfo(Component component) {1125ComponentInfo info = componentInfos.get(component);1126if (info == null) {1127info = new ComponentInfo(component);1128componentInfos.put(component, info);1129if (component.getParent() != host) {1130host.add(component);1131}1132}1133return info;1134}11351136/**1137* Adjusts the autopadding springs for the horizontal and vertical1138* groups. If {@code insert} is {@code true} this will insert auto padding1139* springs, otherwise this will only adjust the springs that1140* comprise auto preferred padding springs.1141*/1142private void insertAutopadding(boolean insert) {1143horizontalGroup.insertAutopadding(HORIZONTAL,1144new ArrayList<AutoPreferredGapSpring>(1),1145new ArrayList<AutoPreferredGapSpring>(1),1146new ArrayList<ComponentSpring>(1),1147new ArrayList<ComponentSpring>(1), insert);1148verticalGroup.insertAutopadding(VERTICAL,1149new ArrayList<AutoPreferredGapSpring>(1),1150new ArrayList<AutoPreferredGapSpring>(1),1151new ArrayList<ComponentSpring>(1),1152new ArrayList<ComponentSpring>(1), insert);1153}11541155/**1156* Returns {@code true} if the two Components have a common ParallelGroup1157* ancestor along the particular axis.1158*/1159private boolean areParallelSiblings(Component source, Component target,1160int axis) {1161ComponentInfo sourceInfo = getComponentInfo(source);1162ComponentInfo targetInfo = getComponentInfo(target);1163Spring sourceSpring;1164Spring targetSpring;1165if (axis == HORIZONTAL) {1166sourceSpring = sourceInfo.horizontalSpring;1167targetSpring = targetInfo.horizontalSpring;1168} else {1169sourceSpring = sourceInfo.verticalSpring;1170targetSpring = targetInfo.verticalSpring;1171}1172Set<Spring> sourcePath = tmpParallelSet;1173sourcePath.clear();1174Spring spring = sourceSpring.getParent();1175while (spring != null) {1176sourcePath.add(spring);1177spring = spring.getParent();1178}1179spring = targetSpring.getParent();1180while (spring != null) {1181if (sourcePath.contains(spring)) {1182sourcePath.clear();1183while (spring != null) {1184if (spring instanceof ParallelGroup) {1185return true;1186}1187spring = spring.getParent();1188}1189return false;1190}1191spring = spring.getParent();1192}1193sourcePath.clear();1194return false;1195}11961197private boolean isLeftToRight() {1198return host.getComponentOrientation().isLeftToRight();1199}12001201/**1202* Returns a string representation of this {@code GroupLayout}.1203* This method is intended to be used for debugging purposes,1204* and the content and format of the returned string may vary1205* between implementations.1206*1207* @return a string representation of this {@code GroupLayout}1208**/1209public String toString() {1210if (springsChanged) {1211registerComponents(horizontalGroup, HORIZONTAL);1212registerComponents(verticalGroup, VERTICAL);1213}1214StringBuffer buffer = new StringBuffer();1215buffer.append("HORIZONTAL\n");1216createSpringDescription(buffer, horizontalGroup, " ", HORIZONTAL);1217buffer.append("\nVERTICAL\n");1218createSpringDescription(buffer, verticalGroup, " ", VERTICAL);1219return buffer.toString();1220}12211222private void createSpringDescription(StringBuffer buffer, Spring spring,1223String indent, int axis) {1224String origin = "";1225String padding = "";1226if (spring instanceof ComponentSpring) {1227ComponentSpring cSpring = (ComponentSpring)spring;1228origin = Integer.toString(cSpring.getOrigin()) + " ";1229String name = cSpring.getComponent().getName();1230if (name != null) {1231origin = "name=" + name + ", ";1232}1233}1234if (spring instanceof AutoPreferredGapSpring) {1235AutoPreferredGapSpring paddingSpring =1236(AutoPreferredGapSpring)spring;1237padding = ", userCreated=" + paddingSpring.getUserCreated() +1238", matches=" + paddingSpring.getMatchDescription();1239}1240buffer.append(indent + spring.getClass().getName() + " " +1241Integer.toHexString(spring.hashCode()) + " " +1242origin +1243", size=" + spring.getSize() +1244", alignment=" + spring.getAlignment() +1245" prefs=[" + spring.getMinimumSize(axis) +1246" " + spring.getPreferredSize(axis) +1247" " + spring.getMaximumSize(axis) +1248padding + "]\n");1249if (spring instanceof Group) {1250List<Spring> springs = ((Group)spring).springs;1251indent += " ";1252for (int counter = 0; counter < springs.size(); counter++) {1253createSpringDescription(buffer, springs.get(counter), indent,1254axis);1255}1256}1257}125812591260/**1261* Spring consists of a range: min, pref and max, a value some where in1262* the middle of that, and a location. Spring caches the1263* min/max/pref. If the min/pref/max has internally changes, or needs1264* to be updated you must invoke clear.1265*/1266private abstract class Spring {1267private int size;1268private int min;1269private int max;1270private int pref;1271private Spring parent;12721273private Alignment alignment;12741275Spring() {1276min = pref = max = UNSET;1277}12781279/**1280* Calculates and returns the minimum size.1281*1282* @param axis the axis of layout; one of HORIZONTAL or VERTICAL1283* @return the minimum size1284*/1285abstract int calculateMinimumSize(int axis);12861287/**1288* Calculates and returns the preferred size.1289*1290* @param axis the axis of layout; one of HORIZONTAL or VERTICAL1291* @return the preferred size1292*/1293abstract int calculatePreferredSize(int axis);12941295/**1296* Calculates and returns the minimum size.1297*1298* @param axis the axis of layout; one of HORIZONTAL or VERTICAL1299* @return the minimum size1300*/1301abstract int calculateMaximumSize(int axis);13021303/**1304* Sets the parent of this Spring.1305*/1306void setParent(Spring parent) {1307this.parent = parent;1308}13091310/**1311* Returns the parent of this spring.1312*/1313Spring getParent() {1314return parent;1315}13161317// This is here purely as a convenience for ParallelGroup to avoid1318// having to track alignment separately.1319void setAlignment(Alignment alignment) {1320this.alignment = alignment;1321}13221323/**1324* Alignment for this Spring, this may be null.1325*/1326Alignment getAlignment() {1327return alignment;1328}13291330/**1331* Returns the minimum size.1332*/1333final int getMinimumSize(int axis) {1334if (min == UNSET) {1335min = constrain(calculateMinimumSize(axis));1336}1337return min;1338}13391340/**1341* Returns the preferred size.1342*/1343final int getPreferredSize(int axis) {1344if (pref == UNSET) {1345pref = constrain(calculatePreferredSize(axis));1346}1347return pref;1348}13491350/**1351* Returns the maximum size.1352*/1353final int getMaximumSize(int axis) {1354if (max == UNSET) {1355max = constrain(calculateMaximumSize(axis));1356}1357return max;1358}13591360/**1361* Sets the value and location of the spring. Subclasses1362* will want to invoke super, then do any additional sizing.1363*1364* @param axis HORIZONTAL or VERTICAL1365* @param origin of this Spring1366* @param size of the Spring. If size is UNSET, this invokes1367* clear.1368*/1369void setSize(int axis, int origin, int size) {1370this.size = size;1371if (size == UNSET) {1372unset();1373}1374}13751376/**1377* Resets the cached min/max/pref.1378*/1379void unset() {1380size = min = pref = max = UNSET;1381}13821383/**1384* Returns the current size.1385*/1386int getSize() {1387return size;1388}13891390int constrain(int value) {1391return Math.min(value, Short.MAX_VALUE);1392}13931394int getBaseline() {1395return -1;1396}13971398BaselineResizeBehavior getBaselineResizeBehavior() {1399return BaselineResizeBehavior.OTHER;1400}14011402final boolean isResizable(int axis) {1403int min = getMinimumSize(axis);1404int pref = getPreferredSize(axis);1405return (min != pref || pref != getMaximumSize(axis));1406}14071408/**1409* Returns {@code true} if this spring will ALWAYS have a zero1410* size. This should NOT check the current size, rather it's1411* meant to quickly test if this Spring will always have a1412* zero size.1413*1414* @param treatAutopaddingAsZeroSized if {@code true}, auto padding1415* springs should be treated as having a size of {@code 0}1416* @return {@code true} if this spring will have a zero size,1417* {@code false} otherwise1418*/1419abstract boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized);1420}14211422/**1423* {@code Group} provides the basis for the two types of1424* operations supported by {@code GroupLayout}: laying out1425* components one after another ({@link SequentialGroup SequentialGroup})1426* or aligned ({@link ParallelGroup ParallelGroup}). {@code Group} and1427* its subclasses have no public constructor; to create one use1428* one of {@code createSequentialGroup} or1429* {@code createParallelGroup}. Additionally, taking a {@code Group}1430* created from one {@code GroupLayout} and using it with another1431* will produce undefined results.1432* <p>1433* Various methods in {@code Group} and its subclasses allow you1434* to explicitly specify the range. The arguments to these methods1435* can take two forms, either a value greater than or equal to 0,1436* or one of {@code DEFAULT_SIZE} or {@code PREFERRED_SIZE}. A1437* value greater than or equal to {@code 0} indicates a specific1438* size. {@code DEFAULT_SIZE} indicates the corresponding size1439* from the component should be used. For example, if {@code1440* DEFAULT_SIZE} is passed as the minimum size argument, the1441* minimum size is obtained from invoking {@code getMinimumSize}1442* on the component. Likewise, {@code PREFERRED_SIZE} indicates1443* the value from {@code getPreferredSize} should be used.1444* The following example adds {@code myComponent} to {@code group}1445* with specific values for the range. That is, the minimum is1446* explicitly specified as 100, preferred as 200, and maximum as1447* 300.1448* <pre>1449* group.addComponent(myComponent, 100, 200, 300);1450* </pre>1451* The following example adds {@code myComponent} to {@code group} using1452* a combination of the forms. The minimum size is forced to be the1453* same as the preferred size, the preferred size is determined by1454* using {@code myComponent.getPreferredSize} and the maximum is1455* determined by invoking {@code getMaximumSize} on the component.1456* <pre>1457* group.addComponent(myComponent, GroupLayout.PREFERRED_SIZE,1458* GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE);1459* </pre>1460* <p>1461* Unless otherwise specified all the methods of {@code Group} and1462* its subclasses that allow you to specify a range throw an1463* {@code IllegalArgumentException} if passed an invalid range. An1464* invalid range is one in which any of the values are < 0 and1465* not one of {@code PREFERRED_SIZE} or {@code DEFAULT_SIZE}, or1466* the following is not met (for specific values): {@code min}1467* <= {@code pref} <= {@code max}.1468* <p>1469* Similarly any methods that take a {@code Component} throw a1470* {@code IllegalArgumentException} if passed {@code null} and any methods1471* that take a {@code Group} throw an {@code NullPointerException} if1472* passed {@code null}.1473*1474* @see #createSequentialGroup1475* @see #createParallelGroup1476* @since 1.61477*/1478public abstract class Group extends Spring {1479// private int origin;1480// private int size;1481List<Spring> springs;14821483Group() {1484springs = new ArrayList<Spring>();1485}14861487/**1488* Adds a {@code Group} to this {@code Group}.1489*1490* @param group the {@code Group} to add1491* @return this {@code Group}1492*/1493public Group addGroup(Group group) {1494return addSpring(group);1495}14961497/**1498* Adds a {@code Component} to this {@code Group}.1499*1500* @param component the {@code Component} to add1501* @return this {@code Group}1502*/1503public Group addComponent(Component component) {1504return addComponent(component, DEFAULT_SIZE, DEFAULT_SIZE,1505DEFAULT_SIZE);1506}15071508/**1509* Adds a {@code Component} to this {@code Group}1510* with the specified size.1511*1512* @param component the {@code Component} to add1513* @param min the minimum size or one of {@code DEFAULT_SIZE} or1514* {@code PREFERRED_SIZE}1515* @param pref the preferred size or one of {@code DEFAULT_SIZE} or1516* {@code PREFERRED_SIZE}1517* @param max the maximum size or one of {@code DEFAULT_SIZE} or1518* {@code PREFERRED_SIZE}1519* @return this {@code Group}1520*/1521public Group addComponent(Component component, int min, int pref,1522int max) {1523return addSpring(new ComponentSpring(component, min, pref, max));1524}15251526/**1527* Adds a rigid gap to this {@code Group}.1528*1529* @param size the size of the gap1530* @return this {@code Group}1531* @throws IllegalArgumentException if {@code size} is less than1532* {@code 0}1533*/1534public Group addGap(int size) {1535return addGap(size, size, size);1536}15371538/**1539* Adds a gap to this {@code Group} with the specified size.1540*1541* @param min the minimum size of the gap1542* @param pref the preferred size of the gap1543* @param max the maximum size of the gap1544* @throws IllegalArgumentException if any of the values are1545* less than {@code 0}1546* @return this {@code Group}1547*/1548public Group addGap(int min, int pref, int max) {1549return addSpring(new GapSpring(min, pref, max));1550}15511552Spring getSpring(int index) {1553return springs.get(index);1554}15551556int indexOf(Spring spring) {1557return springs.indexOf(spring);1558}15591560/**1561* Adds the Spring to the list of {@code Spring}s and returns1562* the receiver.1563*/1564Group addSpring(Spring spring) {1565springs.add(spring);1566spring.setParent(this);1567if (!(spring instanceof AutoPreferredGapSpring) ||1568!((AutoPreferredGapSpring)spring).getUserCreated()) {1569springsChanged = true;1570}1571return this;1572}15731574//1575// Spring methods1576//15771578void setSize(int axis, int origin, int size) {1579super.setSize(axis, origin, size);1580if (size == UNSET) {1581for (int counter = springs.size() - 1; counter >= 0;1582counter--) {1583getSpring(counter).setSize(axis, origin, size);1584}1585} else {1586setValidSize(axis, origin, size);1587}1588}15891590/**1591* This is invoked from {@code setSize} if passed a value1592* other than UNSET.1593*/1594abstract void setValidSize(int axis, int origin, int size);15951596int calculateMinimumSize(int axis) {1597return calculateSize(axis, MIN_SIZE);1598}15991600int calculatePreferredSize(int axis) {1601return calculateSize(axis, PREF_SIZE);1602}16031604int calculateMaximumSize(int axis) {1605return calculateSize(axis, MAX_SIZE);1606}16071608/**1609* Calculates the specified size. This is called from1610* one of the {@code getMinimumSize0},1611* {@code getPreferredSize0} or1612* {@code getMaximumSize0} methods. This will invoke1613* to {@code operator} to combine the values.1614*/1615int calculateSize(int axis, int type) {1616int count = springs.size();1617if (count == 0) {1618return 0;1619}1620if (count == 1) {1621return getSpringSize(getSpring(0), axis, type);1622}1623int size = constrain(operator(getSpringSize(getSpring(0), axis,1624type), getSpringSize(getSpring(1), axis, type)));1625for (int counter = 2; counter < count; counter++) {1626size = constrain(operator(size, getSpringSize(1627getSpring(counter), axis, type)));1628}1629return size;1630}16311632int getSpringSize(Spring spring, int axis, int type) {1633switch(type) {1634case MIN_SIZE:1635return spring.getMinimumSize(axis);1636case PREF_SIZE:1637return spring.getPreferredSize(axis);1638case MAX_SIZE:1639return spring.getMaximumSize(axis);1640}1641assert false;1642return 0;1643}16441645/**1646* Used to compute how the two values representing two springs1647* will be combined. For example, a group that layed things out1648* one after the next would return {@code a + b}.1649*/1650abstract int operator(int a, int b);16511652//1653// Padding1654//16551656/**1657* Adjusts the autopadding springs in this group and its children.1658* If {@code insert} is true this will insert auto padding1659* springs, otherwise this will only adjust the springs that1660* comprise auto preferred padding springs.1661*1662* @param axis the axis of the springs; HORIZONTAL or VERTICAL1663* @param leadingPadding List of AutopaddingSprings that occur before1664* this Group1665* @param trailingPadding any trailing autopadding springs are added1666* to this on exit1667* @param leading List of ComponentSprings that occur before this Group1668* @param trailing any trailing ComponentSpring are added to this1669* List1670* @param insert Whether or not to insert AutopaddingSprings or just1671* adjust any existing AutopaddingSprings.1672*/1673abstract void insertAutopadding(int axis,1674List<AutoPreferredGapSpring> leadingPadding,1675List<AutoPreferredGapSpring> trailingPadding,1676List<ComponentSpring> leading, List<ComponentSpring> trailing,1677boolean insert);16781679/**1680* Removes any AutopaddingSprings for this Group and its children.1681*/1682void removeAutopadding() {1683unset();1684for (int counter = springs.size() - 1; counter >= 0; counter--) {1685Spring spring = springs.get(counter);1686if (spring instanceof AutoPreferredGapSpring) {1687if (((AutoPreferredGapSpring)spring).getUserCreated()) {1688((AutoPreferredGapSpring)spring).reset();1689} else {1690springs.remove(counter);1691}1692} else if (spring instanceof Group) {1693((Group)spring).removeAutopadding();1694}1695}1696}16971698void unsetAutopadding() {1699// Clear cached pref/min/max.1700unset();1701for (int counter = springs.size() - 1; counter >= 0; counter--) {1702Spring spring = springs.get(counter);1703if (spring instanceof AutoPreferredGapSpring) {1704spring.unset();1705} else if (spring instanceof Group) {1706((Group)spring).unsetAutopadding();1707}1708}1709}17101711void calculateAutopadding(int axis) {1712for (int counter = springs.size() - 1; counter >= 0; counter--) {1713Spring spring = springs.get(counter);1714if (spring instanceof AutoPreferredGapSpring) {1715// Force size to be reset.1716spring.unset();1717((AutoPreferredGapSpring)spring).calculatePadding(axis);1718} else if (spring instanceof Group) {1719((Group)spring).calculateAutopadding(axis);1720}1721}1722// Clear cached pref/min/max.1723unset();1724}17251726@Override1727boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {1728for (int i = springs.size() - 1; i >= 0; i--) {1729Spring spring = springs.get(i);1730if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {1731return false;1732}1733}1734return true;1735}1736}173717381739/**1740* A {@code Group} that positions and sizes its elements1741* sequentially, one after another. This class has no public1742* constructor, use the {@code createSequentialGroup} method1743* to create one.1744* <p>1745* In order to align a {@code SequentialGroup} along the baseline1746* of a baseline aligned {@code ParallelGroup} you need to specify1747* which of the elements of the {@code SequentialGroup} is used to1748* determine the baseline. The element used to calculate the1749* baseline is specified using one of the {@code add} methods that1750* take a {@code boolean}. The last element added with a value of1751* {@code true} for {@code useAsBaseline} is used to calculate the1752* baseline.1753*1754* @see #createSequentialGroup1755* @since 1.61756*/1757public class SequentialGroup extends Group {1758private Spring baselineSpring;17591760SequentialGroup() {1761}17621763/**1764* {@inheritDoc}1765*/1766public SequentialGroup addGroup(Group group) {1767return (SequentialGroup)super.addGroup(group);1768}17691770/**1771* Adds a {@code Group} to this {@code Group}.1772*1773* @param group the {@code Group} to add1774* @param useAsBaseline whether the specified {@code Group} should1775* be used to calculate the baseline for this {@code Group}1776* @return this {@code Group}1777*/1778public SequentialGroup addGroup(boolean useAsBaseline, Group group) {1779super.addGroup(group);1780if (useAsBaseline) {1781baselineSpring = group;1782}1783return this;1784}17851786/**1787* {@inheritDoc}1788*/1789public SequentialGroup addComponent(Component component) {1790return (SequentialGroup)super.addComponent(component);1791}17921793/**1794* Adds a {@code Component} to this {@code Group}.1795*1796* @param useAsBaseline whether the specified {@code Component} should1797* be used to calculate the baseline for this {@code Group}1798* @param component the {@code Component} to add1799* @return this {@code Group}1800*/1801public SequentialGroup addComponent(boolean useAsBaseline,1802Component component) {1803super.addComponent(component);1804if (useAsBaseline) {1805baselineSpring = springs.get(springs.size() - 1);1806}1807return this;1808}18091810/**1811* {@inheritDoc}1812*/1813public SequentialGroup addComponent(Component component, int min,1814int pref, int max) {1815return (SequentialGroup)super.addComponent(1816component, min, pref, max);1817}18181819/**1820* Adds a {@code Component} to this {@code Group}1821* with the specified size.1822*1823* @param useAsBaseline whether the specified {@code Component} should1824* be used to calculate the baseline for this {@code Group}1825* @param component the {@code Component} to add1826* @param min the minimum size or one of {@code DEFAULT_SIZE} or1827* {@code PREFERRED_SIZE}1828* @param pref the preferred size or one of {@code DEFAULT_SIZE} or1829* {@code PREFERRED_SIZE}1830* @param max the maximum size or one of {@code DEFAULT_SIZE} or1831* {@code PREFERRED_SIZE}1832* @return this {@code Group}1833*/1834public SequentialGroup addComponent(boolean useAsBaseline,1835Component component, int min, int pref, int max) {1836super.addComponent(component, min, pref, max);1837if (useAsBaseline) {1838baselineSpring = springs.get(springs.size() - 1);1839}1840return this;1841}18421843/**1844* {@inheritDoc}1845*/1846public SequentialGroup addGap(int size) {1847return (SequentialGroup)super.addGap(size);1848}18491850/**1851* {@inheritDoc}1852*/1853public SequentialGroup addGap(int min, int pref, int max) {1854return (SequentialGroup)super.addGap(min, pref, max);1855}18561857/**1858* Adds an element representing the preferred gap between two1859* components. The element created to represent the gap is not1860* resizable.1861*1862* @param comp1 the first component1863* @param comp2 the second component1864* @param type the type of gap; one of the constants defined by1865* {@code LayoutStyle}1866* @return this {@code SequentialGroup}1867* @throws IllegalArgumentException if {@code type}, {@code comp1} or1868* {@code comp2} is {@code null}1869* @see LayoutStyle1870*/1871public SequentialGroup addPreferredGap(JComponent comp1,1872JComponent comp2, ComponentPlacement type) {1873return addPreferredGap(comp1, comp2, type, DEFAULT_SIZE,1874PREFERRED_SIZE);1875}18761877/**1878* Adds an element representing the preferred gap between two1879* components.1880*1881* @param comp1 the first component1882* @param comp2 the second component1883* @param type the type of gap1884* @param pref the preferred size of the grap; one of1885* {@code DEFAULT_SIZE} or a value >= 01886* @param max the maximum size of the gap; one of1887* {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}1888* or a value >= 01889* @return this {@code SequentialGroup}1890* @throws IllegalArgumentException if {@code type}, {@code comp1} or1891* {@code comp2} is {@code null}1892* @see LayoutStyle1893*/1894public SequentialGroup addPreferredGap(JComponent comp1,1895JComponent comp2, ComponentPlacement type, int pref,1896int max) {1897if (type == null) {1898throw new IllegalArgumentException("Type must be non-null");1899}1900if (comp1 == null || comp2 == null) {1901throw new IllegalArgumentException(1902"Components must be non-null");1903}1904checkPreferredGapValues(pref, max);1905return (SequentialGroup)addSpring(new PreferredGapSpring(1906comp1, comp2, type, pref, max));1907}19081909/**1910* Adds an element representing the preferred gap between the1911* nearest components. During layout, neighboring1912* components are found, and the size of the added gap is set1913* based on the preferred gap between the components. If no1914* neighboring components are found the gap has a size of {@code 0}.1915* <p>1916* The element created to represent the gap is not1917* resizable.1918*1919* @param type the type of gap; one of1920* {@code LayoutStyle.ComponentPlacement.RELATED} or1921* {@code LayoutStyle.ComponentPlacement.UNRELATED}1922* @return this {@code SequentialGroup}1923* @see LayoutStyle1924* @throws IllegalArgumentException if {@code type} is not one of1925* {@code LayoutStyle.ComponentPlacement.RELATED} or1926* {@code LayoutStyle.ComponentPlacement.UNRELATED}1927*/1928public SequentialGroup addPreferredGap(ComponentPlacement type) {1929return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);1930}19311932/**1933* Adds an element representing the preferred gap between the1934* nearest components. During layout, neighboring1935* components are found, and the minimum of this1936* gap is set based on the size of the preferred gap between the1937* neighboring components. If no neighboring components are found the1938* minimum size is set to 0.1939*1940* @param type the type of gap; one of1941* {@code LayoutStyle.ComponentPlacement.RELATED} or1942* {@code LayoutStyle.ComponentPlacement.UNRELATED}1943* @param pref the preferred size of the grap; one of1944* {@code DEFAULT_SIZE} or a value >= 01945* @param max the maximum size of the gap; one of1946* {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}1947* or a value >= 01948* @return this {@code SequentialGroup}1949* @throws IllegalArgumentException if {@code type} is not one of1950* {@code LayoutStyle.ComponentPlacement.RELATED} or1951* {@code LayoutStyle.ComponentPlacement.UNRELATED}1952* @see LayoutStyle1953*/1954public SequentialGroup addPreferredGap(ComponentPlacement type,1955int pref, int max) {1956if (type != ComponentPlacement.RELATED &&1957type != ComponentPlacement.UNRELATED) {1958throw new IllegalArgumentException(1959"Type must be one of " +1960"LayoutStyle.ComponentPlacement.RELATED or " +1961"LayoutStyle.ComponentPlacement.UNRELATED");1962}1963checkPreferredGapValues(pref, max);1964hasPreferredPaddingSprings = true;1965return (SequentialGroup)addSpring(new AutoPreferredGapSpring(1966type, pref, max));1967}19681969/**1970* Adds an element representing the preferred gap between an edge1971* the container and components that touch the border of the1972* container. This has no effect if the added gap does not1973* touch an edge of the parent container.1974* <p>1975* The element created to represent the gap is not1976* resizable.1977*1978* @return this {@code SequentialGroup}1979*/1980public SequentialGroup addContainerGap() {1981return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);1982}19831984/**1985* Adds an element representing the preferred gap between one1986* edge of the container and the next or previous {@code1987* Component} with the specified size. This has no1988* effect if the next or previous element is not a {@code1989* Component} and does not touch one edge of the parent1990* container.1991*1992* @param pref the preferred size; one of {@code DEFAULT_SIZE} or a1993* value >= 01994* @param max the maximum size; one of {@code DEFAULT_SIZE},1995* {@code PREFERRED_SIZE} or a value >= 01996* @return this {@code SequentialGroup}1997*/1998public SequentialGroup addContainerGap(int pref, int max) {1999if ((pref < 0 && pref != DEFAULT_SIZE) ||2000(max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||2001(pref >= 0 && max >= 0 && pref > max)) {2002throw new IllegalArgumentException(2003"Pref and max must be either DEFAULT_VALUE " +2004"or >= 0 and pref <= max");2005}2006hasPreferredPaddingSprings = true;2007return (SequentialGroup)addSpring(2008new ContainerAutoPreferredGapSpring(pref, max));2009}20102011int operator(int a, int b) {2012return constrain(a) + constrain(b);2013}20142015void setValidSize(int axis, int origin, int size) {2016int pref = getPreferredSize(axis);2017if (size == pref) {2018// Layout at preferred size2019for (Spring spring : springs) {2020int springPref = spring.getPreferredSize(axis);2021spring.setSize(axis, origin, springPref);2022origin += springPref;2023}2024} else if (springs.size() == 1) {2025Spring spring = getSpring(0);2026spring.setSize(axis, origin, Math.min(2027Math.max(size, spring.getMinimumSize(axis)),2028spring.getMaximumSize(axis)));2029} else if (springs.size() > 1) {2030// Adjust between min/pref2031setValidSizeNotPreferred(axis, origin, size);2032}2033}20342035private void setValidSizeNotPreferred(int axis, int origin, int size) {2036int delta = size - getPreferredSize(axis);2037assert delta != 0;2038boolean useMin = (delta < 0);2039int springCount = springs.size();2040if (useMin) {2041delta *= -1;2042}20432044// The following algorithm if used for resizing springs:2045// 1. Calculate the resizability of each spring (pref - min or2046// max - pref) into a list.2047// 2. Sort the list in ascending order2048// 3. Iterate through each of the resizable Springs, attempting2049// to give them (pref - size) / resizeCount2050// 4. For any Springs that can not accommodate that much space2051// add the remainder back to the amount to distribute and2052// recalculate how must space the remaining springs will get.2053// 5. Set the size of the springs.20542055// First pass, sort the resizable springs into the List resizable2056List<SpringDelta> resizable = buildResizableList(axis, useMin);2057int resizableCount = resizable.size();20582059if (resizableCount > 0) {2060// How much we would like to give each Spring.2061int sDelta = delta / resizableCount;2062// Remaining space.2063int slop = delta - sDelta * resizableCount;2064int[] sizes = new int[springCount];2065int sign = useMin ? -1 : 1;2066// Second pass, accumulate the resulting deltas (relative to2067// preferred) into sizes.2068for (int counter = 0; counter < resizableCount; counter++) {2069SpringDelta springDelta = resizable.get(counter);2070if ((counter + 1) == resizableCount) {2071sDelta += slop;2072}2073springDelta.delta = Math.min(sDelta, springDelta.delta);2074delta -= springDelta.delta;2075if (springDelta.delta != sDelta && counter + 1 <2076resizableCount) {2077// Spring didn't take all the space, reset how much2078// each spring will get.2079sDelta = delta / (resizableCount - counter - 1);2080slop = delta - sDelta * (resizableCount - counter - 1);2081}2082sizes[springDelta.index] = sign * springDelta.delta;2083}20842085// And finally set the size of each spring2086for (int counter = 0; counter < springCount; counter++) {2087Spring spring = getSpring(counter);2088int sSize = spring.getPreferredSize(axis) + sizes[counter];2089spring.setSize(axis, origin, sSize);2090origin += sSize;2091}2092} else {2093// Nothing resizable, use the min or max of each of the2094// springs.2095for (int counter = 0; counter < springCount; counter++) {2096Spring spring = getSpring(counter);2097int sSize;2098if (useMin) {2099sSize = spring.getMinimumSize(axis);2100} else {2101sSize = spring.getMaximumSize(axis);2102}2103spring.setSize(axis, origin, sSize);2104origin += sSize;2105}2106}2107}21082109/**2110* Returns the sorted list of SpringDelta's for the current set of2111* Springs. The list is ordered based on the amount of flexibility of2112* the springs.2113*/2114private List<SpringDelta> buildResizableList(int axis,2115boolean useMin) {2116// First pass, figure out what is resizable2117int size = springs.size();2118List<SpringDelta> sorted = new ArrayList<SpringDelta>(size);2119for (int counter = 0; counter < size; counter++) {2120Spring spring = getSpring(counter);2121int sDelta;2122if (useMin) {2123sDelta = spring.getPreferredSize(axis) -2124spring.getMinimumSize(axis);2125} else {2126sDelta = spring.getMaximumSize(axis) -2127spring.getPreferredSize(axis);2128}2129if (sDelta > 0) {2130sorted.add(new SpringDelta(counter, sDelta));2131}2132}2133Collections.sort(sorted);2134return sorted;2135}21362137private int indexOfNextNonZeroSpring(2138int index, boolean treatAutopaddingAsZeroSized) {2139while (index < springs.size()) {2140Spring spring = springs.get(index);2141if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {2142return index;2143}2144index++;2145}2146return index;2147}21482149@Override2150void insertAutopadding(int axis,2151List<AutoPreferredGapSpring> leadingPadding,2152List<AutoPreferredGapSpring> trailingPadding,2153List<ComponentSpring> leading, List<ComponentSpring> trailing,2154boolean insert) {2155List<AutoPreferredGapSpring> newLeadingPadding =2156new ArrayList<AutoPreferredGapSpring>(leadingPadding);2157List<AutoPreferredGapSpring> newTrailingPadding =2158new ArrayList<AutoPreferredGapSpring>(1);2159List<ComponentSpring> newLeading =2160new ArrayList<ComponentSpring>(leading);2161List<ComponentSpring> newTrailing = null;2162int counter = 0;2163// Warning, this must use springs.size, as it may change during the2164// loop.2165while (counter < springs.size()) {2166Spring spring = getSpring(counter);2167if (spring instanceof AutoPreferredGapSpring) {2168if (newLeadingPadding.size() == 0) {2169// Autopadding spring. Set the sources of the2170// autopadding spring based on newLeading.2171AutoPreferredGapSpring padding =2172(AutoPreferredGapSpring)spring;2173padding.setSources(newLeading);2174newLeading.clear();2175counter = indexOfNextNonZeroSpring(counter + 1, true);2176if (counter == springs.size()) {2177// Last spring in the list, add it to2178// trailingPadding.2179if (!(padding instanceof2180ContainerAutoPreferredGapSpring)) {2181trailingPadding.add(padding);2182}2183} else {2184newLeadingPadding.clear();2185newLeadingPadding.add(padding);2186}2187} else {2188counter = indexOfNextNonZeroSpring(counter + 1, true);2189}2190} else {2191// Not a padding spring2192if (newLeading.size() > 0 && insert) {2193// There's leading ComponentSprings, create an2194// autopadding spring.2195AutoPreferredGapSpring padding =2196new AutoPreferredGapSpring();2197// Force the newly created spring to be considered2198// by NOT incrementing counter2199springs.add(counter, padding);2200continue;2201}2202if (spring instanceof ComponentSpring) {2203// Spring is a Component, make it the target of any2204// leading AutopaddingSpring.2205ComponentSpring cSpring = (ComponentSpring)spring;2206if (!cSpring.isVisible()) {2207counter++;2208continue;2209}2210for (AutoPreferredGapSpring gapSpring : newLeadingPadding) {2211gapSpring.addTarget(cSpring, axis);2212}2213newLeading.clear();2214newLeadingPadding.clear();2215counter = indexOfNextNonZeroSpring(counter + 1, false);2216if (counter == springs.size()) {2217// Last Spring, add it to trailing2218trailing.add(cSpring);2219} else {2220// Not that last Spring, add it to leading2221newLeading.add(cSpring);2222}2223} else if (spring instanceof Group) {2224// Forward call to child Group2225if (newTrailing == null) {2226newTrailing = new ArrayList<ComponentSpring>(1);2227} else {2228newTrailing.clear();2229}2230newTrailingPadding.clear();2231((Group)spring).insertAutopadding(axis,2232newLeadingPadding, newTrailingPadding,2233newLeading, newTrailing, insert);2234newLeading.clear();2235newLeadingPadding.clear();2236counter = indexOfNextNonZeroSpring(2237counter + 1, (newTrailing.size() == 0));2238if (counter == springs.size()) {2239trailing.addAll(newTrailing);2240trailingPadding.addAll(newTrailingPadding);2241} else {2242newLeading.addAll(newTrailing);2243newLeadingPadding.addAll(newTrailingPadding);2244}2245} else {2246// Gap2247newLeadingPadding.clear();2248newLeading.clear();2249counter++;2250}2251}2252}2253}22542255int getBaseline() {2256if (baselineSpring != null) {2257int baseline = baselineSpring.getBaseline();2258if (baseline >= 0) {2259int size = 0;2260for (Spring spring : springs) {2261if (spring == baselineSpring) {2262return size + baseline;2263} else {2264size += spring.getPreferredSize(VERTICAL);2265}2266}2267}2268}2269return -1;2270}22712272BaselineResizeBehavior getBaselineResizeBehavior() {2273if (isResizable(VERTICAL)) {2274if (!baselineSpring.isResizable(VERTICAL)) {2275// Spring to use for baseline isn't resizable. In this case2276// baseline resize behavior can be determined based on how2277// preceding springs resize.2278boolean leadingResizable = false;2279for (Spring spring : springs) {2280if (spring == baselineSpring) {2281break;2282} else if (spring.isResizable(VERTICAL)) {2283leadingResizable = true;2284break;2285}2286}2287boolean trailingResizable = false;2288for (int i = springs.size() - 1; i >= 0; i--) {2289Spring spring = springs.get(i);2290if (spring == baselineSpring) {2291break;2292}2293if (spring.isResizable(VERTICAL)) {2294trailingResizable = true;2295break;2296}2297}2298if (leadingResizable && !trailingResizable) {2299return BaselineResizeBehavior.CONSTANT_DESCENT;2300} else if (!leadingResizable && trailingResizable) {2301return BaselineResizeBehavior.CONSTANT_ASCENT;2302}2303// If we get here, both leading and trailing springs are2304// resizable. Fall through to OTHER.2305} else {2306BaselineResizeBehavior brb = baselineSpring.getBaselineResizeBehavior();2307if (brb == BaselineResizeBehavior.CONSTANT_ASCENT) {2308for (Spring spring : springs) {2309if (spring == baselineSpring) {2310return BaselineResizeBehavior.CONSTANT_ASCENT;2311}2312if (spring.isResizable(VERTICAL)) {2313return BaselineResizeBehavior.OTHER;2314}2315}2316} else if (brb == BaselineResizeBehavior.CONSTANT_DESCENT) {2317for (int i = springs.size() - 1; i >= 0; i--) {2318Spring spring = springs.get(i);2319if (spring == baselineSpring) {2320return BaselineResizeBehavior.CONSTANT_DESCENT;2321}2322if (spring.isResizable(VERTICAL)) {2323return BaselineResizeBehavior.OTHER;2324}2325}2326}2327}2328return BaselineResizeBehavior.OTHER;2329}2330// Not resizable, treat as constant_ascent2331return BaselineResizeBehavior.CONSTANT_ASCENT;2332}23332334private void checkPreferredGapValues(int pref, int max) {2335if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) ||2336(max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||2337(pref >= 0 && max >= 0 && pref > max)) {2338throw new IllegalArgumentException(2339"Pref and max must be either DEFAULT_SIZE, " +2340"PREFERRED_SIZE, or >= 0 and pref <= max");2341}2342}2343}234423452346/**2347* Used by SequentialGroup in calculating resizability of springs.2348*/2349private static final class SpringDelta implements Comparable<SpringDelta> {2350// Original index.2351public final int index;2352// Delta, one of pref - min or max - pref.2353public int delta;23542355public SpringDelta(int index, int delta) {2356this.index = index;2357this.delta = delta;2358}23592360public int compareTo(SpringDelta o) {2361return delta - o.delta;2362}23632364public String toString() {2365return super.toString() + "[index=" + index + ", delta=" +2366delta + "]";2367}2368}236923702371/**2372* A {@code Group} that aligns and sizes it's children.2373* {@code ParallelGroup} aligns it's children in2374* four possible ways: along the baseline, centered, anchored to the2375* leading edge, or anchored to the trailing edge.2376* <h3>Baseline</h3>2377* A {@code ParallelGroup} that aligns it's children along the2378* baseline must first decide where the baseline is2379* anchored. The baseline can either be anchored to the top, or2380* anchored to the bottom of the group. That is, the distance between the2381* baseline and the beginning of the group can be a constant2382* distance, or the distance between the end of the group and the2383* baseline can be a constant distance. The possible choices2384* correspond to the {@code BaselineResizeBehavior} constants2385* {@link2386* java.awt.Component.BaselineResizeBehavior#CONSTANT_ASCENT CONSTANT_ASCENT} and2387* {@link2388* java.awt.Component.BaselineResizeBehavior#CONSTANT_DESCENT CONSTANT_DESCENT}.2389* <p>2390* The baseline anchor may be explicitly specified by the2391* {@code createBaselineGroup} method, or determined based on the elements.2392* If not explicitly specified, the baseline will be anchored to2393* the bottom if all the elements with a baseline, and that are2394* aligned to the baseline, have a baseline resize behavior of2395* {@code CONSTANT_DESCENT}; otherwise the baseline is anchored to the top2396* of the group.2397* <p>2398* Elements aligned to the baseline are resizable if they have have2399* a baseline resize behavior of {@code CONSTANT_ASCENT} or2400* {@code CONSTANT_DESCENT}. Elements with a baseline resize2401* behavior of {@code OTHER} or {@code CENTER_OFFSET} are not resizable.2402* <p>2403* The baseline is calculated based on the preferred height of each2404* of the elements that have a baseline. The baseline is2405* calculated using the following algorithm:2406* {@code max(maxNonBaselineHeight, maxAscent + maxDescent)}, where the2407* {@code maxNonBaselineHeight} is the maximum height of all elements2408* that do not have a baseline, or are not aligned along the baseline.2409* {@code maxAscent} is the maximum ascent (baseline) of all elements that2410* have a baseline and are aligned along the baseline.2411* {@code maxDescent} is the maximum descent (preferred height - baseline)2412* of all elements that have a baseline and are aligned along the baseline.2413* <p>2414* A {@code ParallelGroup} that aligns it's elements along the baseline2415* is only useful along the vertical axis. If you create a2416* baseline group and use it along the horizontal axis an2417* {@code IllegalStateException} is thrown when you ask2418* {@code GroupLayout} for the minimum, preferred or maximum size or2419* attempt to layout the components.2420* <p>2421* Elements that are not aligned to the baseline and smaller than the size2422* of the {@code ParallelGroup} are positioned in one of three2423* ways: centered, anchored to the leading edge, or anchored to the2424* trailing edge.2425*2426* <h3>Non-baseline {@code ParallelGroup}</h3>2427* {@code ParallelGroup}s created with an alignment other than2428* {@code BASELINE} align elements that are smaller than the size2429* of the group in one of three ways: centered, anchored to the2430* leading edge, or anchored to the trailing edge.2431* <p>2432* The leading edge is based on the axis and {@code2433* ComponentOrientation}. For the vertical axis the top edge is2434* always the leading edge, and the bottom edge is always the2435* trailing edge. When the {@code ComponentOrientation} is {@code2436* LEFT_TO_RIGHT}, the leading edge is the left edge and the2437* trailing edge the right edge. A {@code ComponentOrientation} of2438* {@code RIGHT_TO_LEFT} flips the left and right edges. Child2439* elements are aligned based on the specified alignment the2440* element was added with. If you do not specify an alignment, the2441* alignment specified for the {@code ParallelGroup} is used.2442* <p>2443* To align elements along the baseline you {@code createBaselineGroup},2444* or {@code createParallelGroup} with an alignment of {@code BASELINE}.2445* If the group was not created with a baseline alignment, and you attempt2446* to add an element specifying a baseline alignment, an2447* {@code IllegalArgumentException} is thrown.2448*2449* @see #createParallelGroup()2450* @see #createBaselineGroup(boolean,boolean)2451* @since 1.62452*/2453public class ParallelGroup extends Group {2454// How children are layed out.2455private final Alignment childAlignment;2456// Whether or not we're resizable.2457private final boolean resizable;24582459ParallelGroup(Alignment childAlignment, boolean resizable) {2460this.childAlignment = childAlignment;2461this.resizable = resizable;2462}24632464/**2465* {@inheritDoc}2466*/2467public ParallelGroup addGroup(Group group) {2468return (ParallelGroup)super.addGroup(group);2469}24702471/**2472* {@inheritDoc}2473*/2474public ParallelGroup addComponent(Component component) {2475return (ParallelGroup)super.addComponent(component);2476}24772478/**2479* {@inheritDoc}2480*/2481public ParallelGroup addComponent(Component component, int min, int pref,2482int max) {2483return (ParallelGroup)super.addComponent(component, min, pref, max);2484}24852486/**2487* {@inheritDoc}2488*/2489public ParallelGroup addGap(int pref) {2490return (ParallelGroup)super.addGap(pref);2491}24922493/**2494* {@inheritDoc}2495*/2496public ParallelGroup addGap(int min, int pref, int max) {2497return (ParallelGroup)super.addGap(min, pref, max);2498}24992500/**2501* Adds a {@code Group} to this {@code ParallelGroup} with the2502* specified alignment. If the child is smaller than the2503* {@code Group} it is aligned based on the specified2504* alignment.2505*2506* @param alignment the alignment2507* @param group the {@code Group} to add2508* @return this {@code ParallelGroup}2509* @throws IllegalArgumentException if {@code alignment} is2510* {@code null}2511*/2512public ParallelGroup addGroup(Alignment alignment, Group group) {2513checkChildAlignment(alignment);2514group.setAlignment(alignment);2515return (ParallelGroup)addSpring(group);2516}25172518/**2519* Adds a {@code Component} to this {@code ParallelGroup} with2520* the specified alignment.2521*2522* @param alignment the alignment2523* @param component the {@code Component} to add2524* @return this {@code Group}2525* @throws IllegalArgumentException if {@code alignment} is2526* {@code null}2527*/2528public ParallelGroup addComponent(Component component,2529Alignment alignment) {2530return addComponent(component, alignment, DEFAULT_SIZE, DEFAULT_SIZE,2531DEFAULT_SIZE);2532}25332534/**2535* Adds a {@code Component} to this {@code ParallelGroup} with the2536* specified alignment and size.2537*2538* @param alignment the alignment2539* @param component the {@code Component} to add2540* @param min the minimum size2541* @param pref the preferred size2542* @param max the maximum size2543* @throws IllegalArgumentException if {@code alignment} is2544* {@code null}2545* @return this {@code Group}2546*/2547public ParallelGroup addComponent(Component component,2548Alignment alignment, int min, int pref, int max) {2549checkChildAlignment(alignment);2550ComponentSpring spring = new ComponentSpring(component,2551min, pref, max);2552spring.setAlignment(alignment);2553return (ParallelGroup)addSpring(spring);2554}25552556boolean isResizable() {2557return resizable;2558}25592560int operator(int a, int b) {2561return Math.max(a, b);2562}25632564int calculateMinimumSize(int axis) {2565if (!isResizable()) {2566return getPreferredSize(axis);2567}2568return super.calculateMinimumSize(axis);2569}25702571int calculateMaximumSize(int axis) {2572if (!isResizable()) {2573return getPreferredSize(axis);2574}2575return super.calculateMaximumSize(axis);2576}25772578void setValidSize(int axis, int origin, int size) {2579for (Spring spring : springs) {2580setChildSize(spring, axis, origin, size);2581}2582}25832584void setChildSize(Spring spring, int axis, int origin, int size) {2585Alignment alignment = spring.getAlignment();2586int springSize = Math.min(2587Math.max(spring.getMinimumSize(axis), size),2588spring.getMaximumSize(axis));2589if (alignment == null) {2590alignment = childAlignment;2591}2592switch (alignment) {2593case TRAILING:2594spring.setSize(axis, origin + size - springSize,2595springSize);2596break;2597case CENTER:2598spring.setSize(axis, origin +2599(size - springSize) / 2,springSize);2600break;2601default: // LEADING, or BASELINE2602spring.setSize(axis, origin, springSize);2603break;2604}2605}26062607@Override2608void insertAutopadding(int axis,2609List<AutoPreferredGapSpring> leadingPadding,2610List<AutoPreferredGapSpring> trailingPadding,2611List<ComponentSpring> leading, List<ComponentSpring> trailing,2612boolean insert) {2613for (Spring spring : springs) {2614if (spring instanceof ComponentSpring) {2615if (((ComponentSpring)spring).isVisible()) {2616for (AutoPreferredGapSpring gapSpring :2617leadingPadding) {2618gapSpring.addTarget((ComponentSpring)spring, axis);2619}2620trailing.add((ComponentSpring)spring);2621}2622} else if (spring instanceof Group) {2623((Group)spring).insertAutopadding(axis, leadingPadding,2624trailingPadding, leading, trailing, insert);2625} else if (spring instanceof AutoPreferredGapSpring) {2626((AutoPreferredGapSpring)spring).setSources(leading);2627trailingPadding.add((AutoPreferredGapSpring)spring);2628}2629}2630}26312632private void checkChildAlignment(Alignment alignment) {2633checkChildAlignment(alignment, (this instanceof BaselineGroup));2634}26352636private void checkChildAlignment(Alignment alignment,2637boolean allowsBaseline) {2638if (alignment == null) {2639throw new IllegalArgumentException("Alignment must be non-null");2640}2641if (!allowsBaseline && alignment == Alignment.BASELINE) {2642throw new IllegalArgumentException("Alignment must be one of:" +2643"LEADING, TRAILING or CENTER");2644}2645}2646}264726482649/**2650* An extension of {@code ParallelGroup} that aligns its2651* constituent {@code Spring}s along the baseline.2652*/2653private class BaselineGroup extends ParallelGroup {2654// Whether or not all child springs have a baseline2655private boolean allSpringsHaveBaseline;26562657// max(spring.getBaseline()) of all springs aligned along the baseline2658// that have a baseline2659private int prefAscent;26602661// max(spring.getPreferredSize().height - spring.getBaseline()) of all2662// springs aligned along the baseline that have a baseline2663private int prefDescent;26642665// Whether baselineAnchoredToTop was explicitly set2666private boolean baselineAnchorSet;26672668// Whether the baseline is anchored to the top or the bottom.2669// If anchored to the top the baseline is always at prefAscent,2670// otherwise the baseline is at (height - prefDescent)2671private boolean baselineAnchoredToTop;26722673// Whether or not the baseline has been calculated.2674private boolean calcedBaseline;26752676BaselineGroup(boolean resizable) {2677super(Alignment.LEADING, resizable);2678prefAscent = prefDescent = -1;2679calcedBaseline = false;2680}26812682BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) {2683this(resizable);2684this.baselineAnchoredToTop = baselineAnchoredToTop;2685baselineAnchorSet = true;2686}26872688void unset() {2689super.unset();2690prefAscent = prefDescent = -1;2691calcedBaseline = false;2692}26932694void setValidSize(int axis, int origin, int size) {2695checkAxis(axis);2696if (prefAscent == -1) {2697super.setValidSize(axis, origin, size);2698} else {2699// do baseline layout2700baselineLayout(origin, size);2701}2702}27032704int calculateSize(int axis, int type) {2705checkAxis(axis);2706if (!calcedBaseline) {2707calculateBaselineAndResizeBehavior();2708}2709if (type == MIN_SIZE) {2710return calculateMinSize();2711}2712if (type == MAX_SIZE) {2713return calculateMaxSize();2714}2715if (allSpringsHaveBaseline) {2716return prefAscent + prefDescent;2717}2718return Math.max(prefAscent + prefDescent,2719super.calculateSize(axis, type));2720}27212722private void calculateBaselineAndResizeBehavior() {2723// calculate baseline2724prefAscent = 0;2725prefDescent = 0;2726int baselineSpringCount = 0;2727BaselineResizeBehavior resizeBehavior = null;2728for (Spring spring : springs) {2729if (spring.getAlignment() == null ||2730spring.getAlignment() == Alignment.BASELINE) {2731int baseline = spring.getBaseline();2732if (baseline >= 0) {2733if (spring.isResizable(VERTICAL)) {2734BaselineResizeBehavior brb = spring.2735getBaselineResizeBehavior();2736if (resizeBehavior == null) {2737resizeBehavior = brb;2738} else if (brb != resizeBehavior) {2739resizeBehavior = BaselineResizeBehavior.2740CONSTANT_ASCENT;2741}2742}2743prefAscent = Math.max(prefAscent, baseline);2744prefDescent = Math.max(prefDescent, spring.2745getPreferredSize(VERTICAL) - baseline);2746baselineSpringCount++;2747}2748}2749}2750if (!baselineAnchorSet) {2751if (resizeBehavior == BaselineResizeBehavior.CONSTANT_DESCENT){2752this.baselineAnchoredToTop = false;2753} else {2754this.baselineAnchoredToTop = true;2755}2756}2757allSpringsHaveBaseline = (baselineSpringCount == springs.size());2758calcedBaseline = true;2759}27602761private int calculateMaxSize() {2762int maxAscent = prefAscent;2763int maxDescent = prefDescent;2764int nonBaselineMax = 0;2765for (Spring spring : springs) {2766int baseline;2767int springMax = spring.getMaximumSize(VERTICAL);2768if ((spring.getAlignment() == null ||2769spring.getAlignment() == Alignment.BASELINE) &&2770(baseline = spring.getBaseline()) >= 0) {2771int springPref = spring.getPreferredSize(VERTICAL);2772if (springPref != springMax) {2773switch (spring.getBaselineResizeBehavior()) {2774case CONSTANT_ASCENT:2775if (baselineAnchoredToTop) {2776maxDescent = Math.max(maxDescent,2777springMax - baseline);2778}2779break;2780case CONSTANT_DESCENT:2781if (!baselineAnchoredToTop) {2782maxAscent = Math.max(maxAscent,2783springMax - springPref + baseline);2784}2785break;2786default: // CENTER_OFFSET and OTHER, not resizable2787break;2788}2789}2790} else {2791// Not aligned along the baseline, or no baseline.2792nonBaselineMax = Math.max(nonBaselineMax, springMax);2793}2794}2795return Math.max(nonBaselineMax, maxAscent + maxDescent);2796}27972798private int calculateMinSize() {2799int minAscent = 0;2800int minDescent = 0;2801int nonBaselineMin = 0;2802if (baselineAnchoredToTop) {2803minAscent = prefAscent;2804} else {2805minDescent = prefDescent;2806}2807for (Spring spring : springs) {2808int springMin = spring.getMinimumSize(VERTICAL);2809int baseline;2810if ((spring.getAlignment() == null ||2811spring.getAlignment() == Alignment.BASELINE) &&2812(baseline = spring.getBaseline()) >= 0) {2813int springPref = spring.getPreferredSize(VERTICAL);2814BaselineResizeBehavior brb = spring.2815getBaselineResizeBehavior();2816switch (brb) {2817case CONSTANT_ASCENT:2818if (baselineAnchoredToTop) {2819minDescent = Math.max(springMin - baseline,2820minDescent);2821} else {2822minAscent = Math.max(baseline, minAscent);2823}2824break;2825case CONSTANT_DESCENT:2826if (!baselineAnchoredToTop) {2827minAscent = Math.max(2828baseline - (springPref - springMin),2829minAscent);2830} else {2831minDescent = Math.max(springPref - baseline,2832minDescent);2833}2834break;2835default:2836// CENTER_OFFSET and OTHER are !resizable, use2837// the preferred size.2838minAscent = Math.max(baseline, minAscent);2839minDescent = Math.max(springPref - baseline,2840minDescent);2841break;2842}2843} else {2844// Not aligned along the baseline, or no baseline.2845nonBaselineMin = Math.max(nonBaselineMin, springMin);2846}2847}2848return Math.max(nonBaselineMin, minAscent + minDescent);2849}28502851/**2852* Lays out springs that have a baseline along the baseline. All2853* others are centered.2854*/2855private void baselineLayout(int origin, int size) {2856int ascent;2857int descent;2858if (baselineAnchoredToTop) {2859ascent = prefAscent;2860descent = size - ascent;2861} else {2862ascent = size - prefDescent;2863descent = prefDescent;2864}2865for (Spring spring : springs) {2866Alignment alignment = spring.getAlignment();2867if (alignment == null || alignment == Alignment.BASELINE) {2868int baseline = spring.getBaseline();2869if (baseline >= 0) {2870int springMax = spring.getMaximumSize(VERTICAL);2871int springPref = spring.getPreferredSize(VERTICAL);2872int height = springPref;2873int y;2874switch(spring.getBaselineResizeBehavior()) {2875case CONSTANT_ASCENT:2876y = origin + ascent - baseline;2877height = Math.min(descent, springMax -2878baseline) + baseline;2879break;2880case CONSTANT_DESCENT:2881height = Math.min(ascent, springMax -2882springPref + baseline) +2883(springPref - baseline);2884y = origin + ascent +2885(springPref - baseline) - height;2886break;2887default: // CENTER_OFFSET & OTHER, not resizable2888y = origin + ascent - baseline;2889break;2890}2891spring.setSize(VERTICAL, y, height);2892} else {2893setChildSize(spring, VERTICAL, origin, size);2894}2895} else {2896setChildSize(spring, VERTICAL, origin, size);2897}2898}2899}29002901int getBaseline() {2902if (springs.size() > 1) {2903// Force the baseline to be calculated2904getPreferredSize(VERTICAL);2905return prefAscent;2906} else if (springs.size() == 1) {2907return springs.get(0).getBaseline();2908}2909return -1;2910}29112912BaselineResizeBehavior getBaselineResizeBehavior() {2913if (springs.size() == 1) {2914return springs.get(0).getBaselineResizeBehavior();2915}2916if (baselineAnchoredToTop) {2917return BaselineResizeBehavior.CONSTANT_ASCENT;2918}2919return BaselineResizeBehavior.CONSTANT_DESCENT;2920}29212922// If the axis is VERTICAL, throws an IllegalStateException2923private void checkAxis(int axis) {2924if (axis == HORIZONTAL) {2925throw new IllegalStateException(2926"Baseline must be used along vertical axis");2927}2928}2929}293029312932private final class ComponentSpring extends Spring {2933private Component component;2934private int origin;29352936// min/pref/max are either a value >= 0 or one of2937// DEFAULT_SIZE or PREFERRED_SIZE2938private final int min;2939private final int pref;2940private final int max;29412942// Baseline for the component, computed as necessary.2943private int baseline = -1;29442945// Whether or not the size has been requested yet.2946private boolean installed;29472948private ComponentSpring(Component component, int min, int pref,2949int max) {2950this.component = component;2951if (component == null) {2952throw new IllegalArgumentException(2953"Component must be non-null");2954}29552956checkSize(min, pref, max, true);29572958this.min = min;2959this.max = max;2960this.pref = pref;29612962// getComponentInfo makes sure component is a child of the2963// Container GroupLayout is the LayoutManager for.2964getComponentInfo(component);2965}29662967int calculateMinimumSize(int axis) {2968if (isLinked(axis)) {2969return getLinkSize(axis, MIN_SIZE);2970}2971return calculateNonlinkedMinimumSize(axis);2972}29732974int calculatePreferredSize(int axis) {2975if (isLinked(axis)) {2976return getLinkSize(axis, PREF_SIZE);2977}2978int min = getMinimumSize(axis);2979int pref = calculateNonlinkedPreferredSize(axis);2980int max = getMaximumSize(axis);2981return Math.min(max, Math.max(min, pref));2982}29832984int calculateMaximumSize(int axis) {2985if (isLinked(axis)) {2986return getLinkSize(axis, MAX_SIZE);2987}2988return Math.max(getMinimumSize(axis),2989calculateNonlinkedMaximumSize(axis));2990}29912992boolean isVisible() {2993return getComponentInfo(getComponent()).isVisible();2994}29952996int calculateNonlinkedMinimumSize(int axis) {2997if (!isVisible()) {2998return 0;2999}3000if (min >= 0) {3001return min;3002}3003if (min == PREFERRED_SIZE) {3004return calculateNonlinkedPreferredSize(axis);3005}3006assert (min == DEFAULT_SIZE);3007return getSizeAlongAxis(axis, component.getMinimumSize());3008}30093010int calculateNonlinkedPreferredSize(int axis) {3011if (!isVisible()) {3012return 0;3013}3014if (pref >= 0) {3015return pref;3016}3017assert (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE);3018return getSizeAlongAxis(axis, component.getPreferredSize());3019}30203021int calculateNonlinkedMaximumSize(int axis) {3022if (!isVisible()) {3023return 0;3024}3025if (max >= 0) {3026return max;3027}3028if (max == PREFERRED_SIZE) {3029return calculateNonlinkedPreferredSize(axis);3030}3031assert (max == DEFAULT_SIZE);3032return getSizeAlongAxis(axis, component.getMaximumSize());3033}30343035private int getSizeAlongAxis(int axis, Dimension size) {3036return (axis == HORIZONTAL) ? size.width : size.height;3037}30383039private int getLinkSize(int axis, int type) {3040if (!isVisible()) {3041return 0;3042}3043ComponentInfo ci = getComponentInfo(component);3044return ci.getLinkSize(axis, type);3045}30463047void setSize(int axis, int origin, int size) {3048super.setSize(axis, origin, size);3049this.origin = origin;3050if (size == UNSET) {3051baseline = -1;3052}3053}30543055int getOrigin() {3056return origin;3057}30583059void setComponent(Component component) {3060this.component = component;3061}30623063Component getComponent() {3064return component;3065}30663067int getBaseline() {3068if (baseline == -1) {3069Spring horizontalSpring = getComponentInfo(component).3070horizontalSpring;3071int width = horizontalSpring.getPreferredSize(HORIZONTAL);3072int height = getPreferredSize(VERTICAL);3073if (width > 0 && height > 0) {3074baseline = component.getBaseline(width, height);3075}3076}3077return baseline;3078}30793080BaselineResizeBehavior getBaselineResizeBehavior() {3081return getComponent().getBaselineResizeBehavior();3082}30833084private boolean isLinked(int axis) {3085return getComponentInfo(component).isLinked(axis);3086}30873088void installIfNecessary(int axis) {3089if (!installed) {3090installed = true;3091if (axis == HORIZONTAL) {3092getComponentInfo(component).horizontalSpring = this;3093} else {3094getComponentInfo(component).verticalSpring = this;3095}3096}3097}30983099@Override3100boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3101return !isVisible();3102}3103}310431053106/**3107* Spring representing the preferred distance between two components.3108*/3109private class PreferredGapSpring extends Spring {3110private final JComponent source;3111private final JComponent target;3112private final ComponentPlacement type;3113private final int pref;3114private final int max;31153116PreferredGapSpring(JComponent source, JComponent target,3117ComponentPlacement type, int pref, int max) {3118this.source = source;3119this.target = target;3120this.type = type;3121this.pref = pref;3122this.max = max;3123}31243125int calculateMinimumSize(int axis) {3126return getPadding(axis);3127}31283129int calculatePreferredSize(int axis) {3130if (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE) {3131return getMinimumSize(axis);3132}3133int min = getMinimumSize(axis);3134int max = getMaximumSize(axis);3135return Math.min(max, Math.max(min, pref));3136}31373138int calculateMaximumSize(int axis) {3139if (max == PREFERRED_SIZE || max == DEFAULT_SIZE) {3140return getPadding(axis);3141}3142return Math.max(getMinimumSize(axis), max);3143}31443145private int getPadding(int axis) {3146int position;3147if (axis == HORIZONTAL) {3148position = SwingConstants.EAST;3149} else {3150position = SwingConstants.SOUTH;3151}3152return getLayoutStyle0().getPreferredGap(source,3153target, type, position, host);3154}31553156@Override3157boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3158return false;3159}3160}316131623163/**3164* Spring represented a certain amount of space.3165*/3166private class GapSpring extends Spring {3167private final int min;3168private final int pref;3169private final int max;31703171GapSpring(int min, int pref, int max) {3172checkSize(min, pref, max, false);3173this.min = min;3174this.pref = pref;3175this.max = max;3176}31773178int calculateMinimumSize(int axis) {3179if (min == PREFERRED_SIZE) {3180return getPreferredSize(axis);3181}3182return min;3183}31843185int calculatePreferredSize(int axis) {3186return pref;3187}31883189int calculateMaximumSize(int axis) {3190if (max == PREFERRED_SIZE) {3191return getPreferredSize(axis);3192}3193return max;3194}31953196@Override3197boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3198return false;3199}3200}320132023203/**3204* Spring reprensenting the distance between any number of sources and3205* targets. The targets and sources are computed during layout. An3206* instance of this can either be dynamically created when3207* autocreatePadding is true, or explicitly created by the developer.3208*/3209private class AutoPreferredGapSpring extends Spring {3210List<ComponentSpring> sources;3211ComponentSpring source;3212private List<AutoPreferredGapMatch> matches;3213int size;3214int lastSize;3215private final int pref;3216private final int max;3217// Type of gap3218private ComponentPlacement type;3219private boolean userCreated;32203221private AutoPreferredGapSpring() {3222this.pref = PREFERRED_SIZE;3223this.max = PREFERRED_SIZE;3224this.type = ComponentPlacement.RELATED;3225}32263227AutoPreferredGapSpring(int pref, int max) {3228this.pref = pref;3229this.max = max;3230}32313232AutoPreferredGapSpring(ComponentPlacement type, int pref, int max) {3233this.type = type;3234this.pref = pref;3235this.max = max;3236this.userCreated = true;3237}32383239public void setSource(ComponentSpring source) {3240this.source = source;3241}32423243public void setSources(List<ComponentSpring> sources) {3244this.sources = new ArrayList<ComponentSpring>(sources);3245}32463247public void setUserCreated(boolean userCreated) {3248this.userCreated = userCreated;3249}32503251public boolean getUserCreated() {3252return userCreated;3253}32543255void unset() {3256lastSize = getSize();3257super.unset();3258size = 0;3259}32603261public void reset() {3262size = 0;3263sources = null;3264source = null;3265matches = null;3266}32673268public void calculatePadding(int axis) {3269size = UNSET;3270int maxPadding = UNSET;3271if (matches != null) {3272LayoutStyle p = getLayoutStyle0();3273int position;3274if (axis == HORIZONTAL) {3275if (isLeftToRight()) {3276position = SwingConstants.EAST;3277} else {3278position = SwingConstants.WEST;3279}3280} else {3281position = SwingConstants.SOUTH;3282}3283for (int i = matches.size() - 1; i >= 0; i--) {3284AutoPreferredGapMatch match = matches.get(i);3285maxPadding = Math.max(maxPadding,3286calculatePadding(p, position, match.source,3287match.target));3288}3289}3290if (size == UNSET) {3291size = 0;3292}3293if (maxPadding == UNSET) {3294maxPadding = 0;3295}3296if (lastSize != UNSET) {3297size += Math.min(maxPadding, lastSize);3298}3299}33003301private int calculatePadding(LayoutStyle p, int position,3302ComponentSpring source,3303ComponentSpring target) {3304int delta = target.getOrigin() - (source.getOrigin() +3305source.getSize());3306if (delta >= 0) {3307int padding;3308if ((source.getComponent() instanceof JComponent) &&3309(target.getComponent() instanceof JComponent)) {3310padding = p.getPreferredGap(3311(JComponent)source.getComponent(),3312(JComponent)target.getComponent(), type, position,3313host);3314} else {3315padding = 10;3316}3317if (padding > delta) {3318size = Math.max(size, padding - delta);3319}3320return padding;3321}3322return 0;3323}33243325public void addTarget(ComponentSpring spring, int axis) {3326int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL;3327if (source != null) {3328if (areParallelSiblings(source.getComponent(),3329spring.getComponent(), oAxis)) {3330addValidTarget(source, spring);3331}3332} else {3333Component component = spring.getComponent();3334for (int counter = sources.size() - 1; counter >= 0;3335counter--){3336ComponentSpring source = sources.get(counter);3337if (areParallelSiblings(source.getComponent(),3338component, oAxis)) {3339addValidTarget(source, spring);3340}3341}3342}3343}33443345private void addValidTarget(ComponentSpring source,3346ComponentSpring target) {3347if (matches == null) {3348matches = new ArrayList<AutoPreferredGapMatch>(1);3349}3350matches.add(new AutoPreferredGapMatch(source, target));3351}33523353int calculateMinimumSize(int axis) {3354return size;3355}33563357int calculatePreferredSize(int axis) {3358if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) {3359return size;3360}3361return Math.max(size, pref);3362}33633364int calculateMaximumSize(int axis) {3365if (max >= 0) {3366return Math.max(getPreferredSize(axis), max);3367}3368return size;3369}33703371String getMatchDescription() {3372return (matches == null) ? "" : matches.toString();3373}33743375public String toString() {3376return super.toString() + getMatchDescription();3377}33783379@Override3380boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {3381return treatAutopaddingAsZeroSized;3382}3383}338433853386/**3387* Represents two springs that should have autopadding inserted between3388* them.3389*/3390private final static class AutoPreferredGapMatch {3391public final ComponentSpring source;3392public final ComponentSpring target;33933394AutoPreferredGapMatch(ComponentSpring source, ComponentSpring target) {3395this.source = source;3396this.target = target;3397}33983399private String toString(ComponentSpring spring) {3400return spring.getComponent().getName();3401}34023403public String toString() {3404return "[" + toString(source) + "-" + toString(target) + "]";3405}3406}340734083409/**3410* An extension of AutopaddingSpring used for container level padding.3411*/3412private class ContainerAutoPreferredGapSpring extends3413AutoPreferredGapSpring {3414private List<ComponentSpring> targets;34153416ContainerAutoPreferredGapSpring() {3417super();3418setUserCreated(true);3419}34203421ContainerAutoPreferredGapSpring(int pref, int max) {3422super(pref, max);3423setUserCreated(true);3424}34253426public void addTarget(ComponentSpring spring, int axis) {3427if (targets == null) {3428targets = new ArrayList<ComponentSpring>(1);3429}3430targets.add(spring);3431}34323433public void calculatePadding(int axis) {3434LayoutStyle p = getLayoutStyle0();3435int maxPadding = 0;3436int position;3437size = 0;3438if (targets != null) {3439// Leading3440if (axis == HORIZONTAL) {3441if (isLeftToRight()) {3442position = SwingConstants.WEST;3443} else {3444position = SwingConstants.EAST;3445}3446} else {3447position = SwingConstants.SOUTH;3448}3449for (int i = targets.size() - 1; i >= 0; i--) {3450ComponentSpring targetSpring = targets.get(i);3451int padding = 10;3452if (targetSpring.getComponent() instanceof JComponent) {3453padding = p.getContainerGap(3454(JComponent)targetSpring.getComponent(),3455position, host);3456maxPadding = Math.max(padding, maxPadding);3457padding -= targetSpring.getOrigin();3458} else {3459maxPadding = Math.max(padding, maxPadding);3460}3461size = Math.max(size, padding);3462}3463} else {3464// Trailing3465if (axis == HORIZONTAL) {3466if (isLeftToRight()) {3467position = SwingConstants.EAST;3468} else {3469position = SwingConstants.WEST;3470}3471} else {3472position = SwingConstants.SOUTH;3473}3474if (sources != null) {3475for (int i = sources.size() - 1; i >= 0; i--) {3476ComponentSpring sourceSpring = sources.get(i);3477maxPadding = Math.max(maxPadding,3478updateSize(p, sourceSpring, position));3479}3480} else if (source != null) {3481maxPadding = updateSize(p, source, position);3482}3483}3484if (lastSize != UNSET) {3485size += Math.min(maxPadding, lastSize);3486}3487}34883489private int updateSize(LayoutStyle p, ComponentSpring sourceSpring,3490int position) {3491int padding = 10;3492if (sourceSpring.getComponent() instanceof JComponent) {3493padding = p.getContainerGap(3494(JComponent)sourceSpring.getComponent(), position,3495host);3496}3497int delta = Math.max(0, getParent().getSize() -3498sourceSpring.getSize() - sourceSpring.getOrigin());3499size = Math.max(size, padding - delta);3500return padding;3501}35023503String getMatchDescription() {3504if (targets != null) {3505return "leading: " + targets.toString();3506}3507if (sources != null) {3508return "trailing: " + sources.toString();3509}3510return "--";3511}3512}351335143515// LinkInfo contains the set of ComponentInfosthat are linked along a3516// particular axis.3517private static class LinkInfo {3518private final int axis;3519private final List<ComponentInfo> linked;3520private int size;35213522LinkInfo(int axis) {3523linked = new ArrayList<ComponentInfo>();3524size = UNSET;3525this.axis = axis;3526}35273528public void add(ComponentInfo child) {3529LinkInfo childMaster = child.getLinkInfo(axis, false);3530if (childMaster == null) {3531linked.add(child);3532child.setLinkInfo(axis, this);3533} else if (childMaster != this) {3534linked.addAll(childMaster.linked);3535for (ComponentInfo childInfo : childMaster.linked) {3536childInfo.setLinkInfo(axis, this);3537}3538}3539clearCachedSize();3540}35413542public void remove(ComponentInfo info) {3543linked.remove(info);3544info.setLinkInfo(axis, null);3545if (linked.size() == 1) {3546linked.get(0).setLinkInfo(axis, null);3547}3548clearCachedSize();3549}35503551public void clearCachedSize() {3552size = UNSET;3553}35543555public int getSize(int axis) {3556if (size == UNSET) {3557size = calculateLinkedSize(axis);3558}3559return size;3560}35613562private int calculateLinkedSize(int axis) {3563int size = 0;3564for (ComponentInfo info : linked) {3565ComponentSpring spring;3566if (axis == HORIZONTAL) {3567spring = info.horizontalSpring;3568} else {3569assert (axis == VERTICAL);3570spring = info.verticalSpring;3571}3572size = Math.max(size,3573spring.calculateNonlinkedPreferredSize(axis));3574}3575return size;3576}3577}35783579/**3580* Tracks the horizontal/vertical Springs for a Component.3581* This class is also used to handle Springs that have their sizes3582* linked.3583*/3584private class ComponentInfo {3585// Component being layed out3586private Component component;35873588ComponentSpring horizontalSpring;3589ComponentSpring verticalSpring;35903591// If the component's size is linked to other components, the3592// horizontalMaster and/or verticalMaster reference the group of3593// linked components.3594private LinkInfo horizontalMaster;3595private LinkInfo verticalMaster;35963597private boolean visible;3598private Boolean honorsVisibility;35993600ComponentInfo(Component component) {3601this.component = component;3602updateVisibility();3603}36043605public void dispose() {3606// Remove horizontal/vertical springs3607removeSpring(horizontalSpring);3608horizontalSpring = null;3609removeSpring(verticalSpring);3610verticalSpring = null;3611// Clean up links3612if (horizontalMaster != null) {3613horizontalMaster.remove(this);3614}3615if (verticalMaster != null) {3616verticalMaster.remove(this);3617}3618}36193620void setHonorsVisibility(Boolean honorsVisibility) {3621this.honorsVisibility = honorsVisibility;3622}36233624private void removeSpring(Spring spring) {3625if (spring != null) {3626((Group)spring.getParent()).springs.remove(spring);3627}3628}36293630public boolean isVisible() {3631return visible;3632}36333634/**3635* Updates the cached visibility.3636*3637* @return true if the visibility changed3638*/3639boolean updateVisibility() {3640boolean honorsVisibility;3641if (this.honorsVisibility == null) {3642honorsVisibility = GroupLayout.this.getHonorsVisibility();3643} else {3644honorsVisibility = this.honorsVisibility;3645}3646boolean newVisible = (honorsVisibility) ?3647component.isVisible() : true;3648if (visible != newVisible) {3649visible = newVisible;3650return true;3651}3652return false;3653}36543655public void setBounds(Insets insets, int parentWidth, boolean ltr) {3656int x = horizontalSpring.getOrigin();3657int w = horizontalSpring.getSize();3658int y = verticalSpring.getOrigin();3659int h = verticalSpring.getSize();36603661if (!ltr) {3662x = parentWidth - x - w;3663}3664component.setBounds(x + insets.left, y + insets.top, w, h);3665}36663667public void setComponent(Component component) {3668this.component = component;3669if (horizontalSpring != null) {3670horizontalSpring.setComponent(component);3671}3672if (verticalSpring != null) {3673verticalSpring.setComponent(component);3674}3675}36763677public Component getComponent() {3678return component;3679}36803681/**3682* Returns true if this component has its size linked to3683* other components.3684*/3685public boolean isLinked(int axis) {3686if (axis == HORIZONTAL) {3687return horizontalMaster != null;3688}3689assert (axis == VERTICAL);3690return (verticalMaster != null);3691}36923693private void setLinkInfo(int axis, LinkInfo linkInfo) {3694if (axis == HORIZONTAL) {3695horizontalMaster = linkInfo;3696} else {3697assert (axis == VERTICAL);3698verticalMaster = linkInfo;3699}3700}37013702public LinkInfo getLinkInfo(int axis) {3703return getLinkInfo(axis, true);3704}37053706private LinkInfo getLinkInfo(int axis, boolean create) {3707if (axis == HORIZONTAL) {3708if (horizontalMaster == null && create) {3709// horizontalMaster field is directly set by adding3710// us to the LinkInfo.3711new LinkInfo(HORIZONTAL).add(this);3712}3713return horizontalMaster;3714} else {3715assert (axis == VERTICAL);3716if (verticalMaster == null && create) {3717// verticalMaster field is directly set by adding3718// us to the LinkInfo.3719new LinkInfo(VERTICAL).add(this);3720}3721return verticalMaster;3722}3723}37243725public void clearCachedSize() {3726if (horizontalMaster != null) {3727horizontalMaster.clearCachedSize();3728}3729if (verticalMaster != null) {3730verticalMaster.clearCachedSize();3731}3732}37333734int getLinkSize(int axis, int type) {3735if (axis == HORIZONTAL) {3736return horizontalMaster.getSize(axis);3737} else {3738assert (axis == VERTICAL);3739return verticalMaster.getSize(axis);3740}3741}37423743}3744}374537463747