alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (GroupLayout.java)

This example Java source code file (GroupLayout.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

arraylist, autopreferredgapspring, awt, componentinfo, componentspring, default_size, group, horizontal, illegalargumentexception, list, parallelgroup, preferred_size, sequentialgroup, spring, unset, util

The GroupLayout.java Java example source code

/*
 * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package javax.swing;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.util.*;
import static java.awt.Component.BaselineResizeBehavior;
import static javax.swing.LayoutStyle.ComponentPlacement;
import static javax.swing.SwingConstants.HORIZONTAL;
import static javax.swing.SwingConstants.VERTICAL;

/**
 * {@code GroupLayout} is a {@code LayoutManager} that hierarchically
 * groups components in order to position them in a {@code Container}.
 * {@code GroupLayout} is intended for use by builders, but may be
 * hand-coded as well.
 * Grouping is done by instances of the {@link Group Group} class. {@code
 * GroupLayout} supports two types of groups. A sequential group
 * positions its child elements sequentially, one after another. A
 * parallel group aligns its child elements in one of four ways.
 * <p>
 * Each group may contain any number of elements, where an element is
 * a {@code Group}, {@code Component}, or gap. A gap can be thought
 * of as an invisible component with a minimum, preferred and maximum
 * size. In addition {@code GroupLayout} supports a preferred gap,
 * whose value comes from {@code LayoutStyle}.
 * <p>
 * Elements are similar to a spring. Each element has a range as
 * specified by a minimum, preferred and maximum.  Gaps have either a
 * developer-specified range, or a range determined by {@code
 * LayoutStyle}. The range for {@code Component}s is determined from
 * the {@code Component}'s {@code getMinimumSize}, {@code
 * getPreferredSize} and {@code getMaximumSize} methods. In addition,
 * when adding {@code Component}s you may specify a particular range
 * to use instead of that from the component. The range for a {@code
 * Group} is determined by the type of group. A {@code ParallelGroup}'s
 * range is the maximum of the ranges of its elements. A {@code
 * SequentialGroup}'s range is the sum of the ranges of its elements.
 * <p>
 * {@code GroupLayout} treats each axis independently.  That is, there
 * is a group representing the horizontal axis, and a group
 * representing the vertical axis.  The horizontal group is
 * responsible for determining the minimum, preferred and maximum size
 * along the horizontal axis as well as setting the x and width of the
 * components contained in it. The vertical group is responsible for
 * determining the minimum, preferred and maximum size along the
 * vertical axis as well as setting the y and height of the
 * components contained in it. Each {@code Component} must exist in both
 * a horizontal and vertical group, otherwise an {@code IllegalStateException}
 * is thrown during layout, or when the minimum, preferred or
 * maximum size is requested.
 * <p>
 * The following diagram shows a sequential group along the horizontal
 * axis. The sequential group contains three components. A parallel group
 * was used along the vertical axis.
 * <p style="text-align:center">
 * <img src="doc-files/groupLayout.1.gif" alt="Sequential group along the horizontal axis in three components">
 * <p>
 * To reinforce that each axis is treated independently the diagram shows
 * the range of each group and element along each axis. The
 * range of each component has been projected onto the axes,
 * and the groups are rendered in blue (horizontal) and red (vertical).
 * For readability there is a gap between each of the elements in the
 * sequential group.
 * <p>
 * The sequential group along the horizontal axis is rendered as a solid
 * blue line. Notice the sequential group is the sum of the children elements
 * it contains.
 * <p>
 * Along the vertical axis the parallel group is the maximum of the height
 * of each of the components. As all three components have the same height,
 * the parallel group has the same height.
 * <p>
 * The following diagram shows the same three components, but with the
 * parallel group along the horizontal axis and the sequential group along
 * the vertical axis.
 *
 * <p style="text-align:center">
 * <img src="doc-files/groupLayout.2.gif" alt="Sequential group along the vertical axis in three components">
 * <p>
 * As {@code c1} is the largest of the three components, the parallel
 * group is sized to {@code c1}. As {@code c2} and {@code c3} are smaller
 * than {@code c1} they are aligned based on the alignment specified
 * for the component (if specified) or the default alignment of the
 * parallel group. In the diagram {@code c2} and {@code c3} were created
 * with an alignment of {@code LEADING}. If the component orientation were
 * right-to-left then {@code c2} and {@code c3} would be positioned on
 * the opposite side.
 * <p>
 * The following diagram shows a sequential group along both the horizontal
 * and vertical axis.
 * <p style="text-align:center">
 * <img src="doc-files/groupLayout.3.gif" alt="Sequential group along both the horizontal and vertical axis in three components">
 * <p>
 * {@code GroupLayout} provides the ability to insert gaps between
 * {@code Component}s. The size of the gap is determined by an
 * instance of {@code LayoutStyle}. This may be turned on using the
 * {@code setAutoCreateGaps} method.  Similarly, you may use
 * the {@code setAutoCreateContainerGaps} method to insert gaps
 * between components that touch the edge of the parent container and the
 * container.
 * <p>
 * The following builds a panel consisting of two labels in
 * one column, followed by two textfields in the next column:
 * <pre>
 *   JComponent panel = ...;
 *   GroupLayout layout = new GroupLayout(panel);
 *   panel.setLayout(layout);
 *
 *   // Turn on automatically adding gaps between components
 *   layout.setAutoCreateGaps(true);
 *
 *   // Turn on automatically creating gaps between components that touch
 *   // the edge of the container and the container.
 *   layout.setAutoCreateContainerGaps(true);
 *
 *   // Create a sequential group for the horizontal axis.
 *
 *   GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
 *
 *   // The sequential group in turn contains two parallel groups.
 *   // One parallel group contains the labels, the other the text fields.
 *   // Putting the labels in a parallel group along the horizontal axis
 *   // positions them at the same x location.
 *   //
 *   // Variable indentation is used to reinforce the level of grouping.
 *   hGroup.addGroup(layout.createParallelGroup().
 *            addComponent(label1).addComponent(label2));
 *   hGroup.addGroup(layout.createParallelGroup().
 *            addComponent(tf1).addComponent(tf2));
 *   layout.setHorizontalGroup(hGroup);
 *
 *   // Create a sequential group for the vertical axis.
 *   GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
 *
 *   // The sequential group contains two parallel groups that align
 *   // the contents along the baseline. The first parallel group contains
 *   // the first label and text field, and the second parallel group contains
 *   // the second label and text field. By using a sequential group
 *   // the labels and text fields are positioned vertically after one another.
 *   vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
 *            addComponent(label1).addComponent(tf1));
 *   vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
 *            addComponent(label2).addComponent(tf2));
 *   layout.setVerticalGroup(vGroup);
 * </pre>
 * <p>
 * When run the following is produced.
 * <p style="text-align:center">
 * <img src="doc-files/groupLayout.example.png" alt="Produced horizontal/vertical form">
 * <p>
 * This layout consists of the following.
 * <ul>
  • The horizontal axis consists of a sequential group containing two * parallel groups. The first parallel group contains the labels, * and the second parallel group contains the text fields. * <li>The vertical axis consists of a sequential group * containing two parallel groups. The parallel groups are configured * to align their components along the baseline. The first parallel * group contains the first label and first text field, and * the second group consists of the second label and second * text field. * </ul> * There are a couple of things to notice in this code: * <ul> * <li>You need not explicitly add the components to the container; this * is indirectly done by using one of the {@code add} methods of * {@code Group}. * <li>The various {@code add} methods return * the caller. This allows for easy chaining of invocations. For * example, {@code group.addComponent(label1).addComponent(label2);} is * equivalent to * {@code group.addComponent(label1); group.addComponent(label2);}. * <li>There are no public constructors for {@code Group}s; instead * use the create methods of {@code GroupLayout}. * </ul> * * @author Tomas Pavek * @author Jan Stola * @author Scott Violet * @since 1.6 */ public class GroupLayout implements LayoutManager2 { // Used in size calculations private static final int MIN_SIZE = 0; private static final int PREF_SIZE = 1; private static final int MAX_SIZE = 2; // Used by prepare, indicates min, pref or max isn't going to be used. private static final int SPECIFIC_SIZE = 3; private static final int UNSET = Integer.MIN_VALUE; /** * Indicates the size from the component or gap should be used for a * particular range value. * * @see Group */ public static final int DEFAULT_SIZE = -1; /** * Indicates the preferred size from the component or gap should * be used for a particular range value. * * @see Group */ public static final int PREFERRED_SIZE = -2; // Whether or not we automatically try and create the preferred // padding between components. private boolean autocreatePadding; // Whether or not we automatically try and create the preferred // padding between components the touch the edge of the container and // the container. private boolean autocreateContainerPadding; /** * Group responsible for layout along the horizontal axis. This is NOT * the user specified group, use getHorizontalGroup to dig that out. */ private Group horizontalGroup; /** * Group responsible for layout along the vertical axis. This is NOT * the user specified group, use getVerticalGroup to dig that out. */ private Group verticalGroup; // Maps from Component to ComponentInfo. This is used for tracking // information specific to a Component. private Map<Component,ComponentInfo> componentInfos; // Container we're doing layout for. private Container host; // Used by areParallelSiblings, cached to avoid excessive garbage. private Set<Spring> tmpParallelSet; // Indicates Springs have changed in some way since last change. private boolean springsChanged; // Indicates invalidateLayout has been invoked. private boolean isValid; // Whether or not any preferred padding (or container padding) springs // exist private boolean hasPreferredPaddingSprings; /** * The LayoutStyle instance to use, if null the sharedInstance is used. */ private LayoutStyle layoutStyle; /** * If true, components that are not visible are treated as though they * aren't there. */ private boolean honorsVisibility; /** * Enumeration of the possible ways {@code ParallelGroup} can align * its children. * * @see #createParallelGroup(Alignment) * @since 1.6 */ public enum Alignment { /** * Indicates the elements should be * aligned to the origin. For the horizontal axis with a left to * right orientation this means aligned to the left edge. For the * vertical axis leading means aligned to the top edge. * * @see #createParallelGroup(Alignment) */ LEADING, /** * Indicates the elements should be aligned to the end of the * region. For the horizontal axis with a left to right * orientation this means aligned to the right edge. For the * vertical axis trailing means aligned to the bottom edge. * * @see #createParallelGroup(Alignment) */ TRAILING, /** * Indicates the elements should be centered in * the region. * * @see #createParallelGroup(Alignment) */ CENTER, /** * Indicates the elements should be aligned along * their baseline. * * @see #createParallelGroup(Alignment) * @see #createBaselineGroup(boolean,boolean) */ BASELINE } private static void checkSize(int min, int pref, int max, boolean isComponentSpring) { checkResizeType(min, isComponentSpring); if (!isComponentSpring && pref < 0) { throw new IllegalArgumentException("Pref must be >= 0"); } else if (isComponentSpring) { checkResizeType(pref, true); } checkResizeType(max, isComponentSpring); checkLessThan(min, pref); checkLessThan(pref, max); } private static void checkResizeType(int type, boolean isComponentSpring) { if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE && type != PREFERRED_SIZE) || (!isComponentSpring && type != PREFERRED_SIZE))) { throw new IllegalArgumentException("Invalid size"); } } private static void checkLessThan(int min, int max) { if (min >= 0 && max >= 0 && min > max) { throw new IllegalArgumentException( "Following is not met: min<=pref<=max"); } } /** * Creates a {@code GroupLayout} for the specified {@code Container}. * * @param host the {@code Container} the {@code GroupLayout} is * the {@code LayoutManager} for * @throws IllegalArgumentException if host is {@code null} */ public GroupLayout(Container host) { if (host == null) { throw new IllegalArgumentException("Container must be non-null"); } honorsVisibility = true; this.host = host; setHorizontalGroup(createParallelGroup(Alignment.LEADING, true)); setVerticalGroup(createParallelGroup(Alignment.LEADING, true)); componentInfos = new HashMap<Component,ComponentInfo>(); tmpParallelSet = new HashSet<Spring>(); } /** * Sets whether component visibility is considered when sizing and * positioning components. A value of {@code true} indicates that * non-visible components should not be treated as part of the * layout. A value of {@code false} indicates that components should be * positioned and sized regardless of visibility. * <p> * A value of {@code false} is useful when the visibility of components * is dynamically adjusted and you don't want surrounding components and * the sizing to change. * <p> * The specified value is used for components that do not have an * explicit visibility specified. * <p> * The default is {@code true}. * * @param honorsVisibility whether component visibility is considered when * sizing and positioning components * @see #setHonorsVisibility(Component,Boolean) */ public void setHonorsVisibility(boolean honorsVisibility) { if (this.honorsVisibility != honorsVisibility) { this.honorsVisibility = honorsVisibility; springsChanged = true; isValid = false; invalidateHost(); } } /** * Returns whether component visibility is considered when sizing and * positioning components. * * @return whether component visibility is considered when sizing and * positioning components */ public boolean getHonorsVisibility() { return honorsVisibility; } /** * Sets whether the component's visibility is considered for * sizing and positioning. A value of {@code Boolean.TRUE} * indicates that if {@code component} is not visible it should * not be treated as part of the layout. A value of {@code false} * indicates that {@code component} is positioned and sized * regardless of it's visibility. A value of {@code null} * indicates the value specified by the single argument method {@code * setHonorsVisibility} should be used. * <p> * If {@code component} is not a child of the {@code Container} this * {@code GroupLayout} is managing, it will be added to the * {@code Container}. * * @param component the component * @param honorsVisibility whether visibility of this {@code component} should be * considered for sizing and positioning * @throws IllegalArgumentException if {@code component} is {@code null} * @see #setHonorsVisibility(Component,Boolean) */ public void setHonorsVisibility(Component component, Boolean honorsVisibility) { if (component == null) { throw new IllegalArgumentException("Component must be non-null"); } getComponentInfo(component).setHonorsVisibility(honorsVisibility); springsChanged = true; isValid = false; invalidateHost(); } /** * Sets whether a gap between components should automatically be * created. For example, if this is {@code true} and you add two * components to a {@code SequentialGroup} a gap between the * two components is automatically be created. The default is * {@code false}. * * @param autoCreatePadding whether a gap between components is * automatically created */ public void setAutoCreateGaps(boolean autoCreatePadding) { if (this.autocreatePadding != autoCreatePadding) { this.autocreatePadding = autoCreatePadding; invalidateHost(); } } /** * Returns {@code true} if gaps between components are automatically * created. * * @return {@code true} if gaps between components are automatically * created */ public boolean getAutoCreateGaps() { return autocreatePadding; } /** * Sets whether a gap between the container and components that * touch the border of the container should automatically be * created. The default is {@code false}. * * @param autoCreateContainerPadding whether a gap between the container and * components that touch the border of the container should * automatically be created */ public void setAutoCreateContainerGaps(boolean autoCreateContainerPadding){ if (this.autocreateContainerPadding != autoCreateContainerPadding) { this.autocreateContainerPadding = autoCreateContainerPadding; horizontalGroup = createTopLevelGroup(getHorizontalGroup()); verticalGroup = createTopLevelGroup(getVerticalGroup()); invalidateHost(); } } /** * Returns {@code true} if gaps between the container and components that * border the container are automatically created. * * @return {@code true} if gaps between the container and components that * border the container are automatically created */ public boolean getAutoCreateContainerGaps() { return autocreateContainerPadding; } /** * Sets the {@code Group} that positions and sizes * components along the horizontal axis. * * @param group the {@code Group} that positions and sizes * components along the horizontal axis * @throws IllegalArgumentException if group is {@code null} */ public void setHorizontalGroup(Group group) { if (group == null) { throw new IllegalArgumentException("Group must be non-null"); } horizontalGroup = createTopLevelGroup(group); invalidateHost(); } /** * Returns the {@code Group} that positions and sizes components * along the horizontal axis. * * @return the {@code Group} responsible for positioning and * sizing component along the horizontal axis */ private Group getHorizontalGroup() { int index = 0; if (horizontalGroup.springs.size() > 1) { index = 1; } return (Group)horizontalGroup.springs.get(index); } /** * Sets the {@code Group} that positions and sizes * components along the vertical axis. * * @param group the {@code Group} that positions and sizes * components along the vertical axis * @throws IllegalArgumentException if group is {@code null} */ public void setVerticalGroup(Group group) { if (group == null) { throw new IllegalArgumentException("Group must be non-null"); } verticalGroup = createTopLevelGroup(group); invalidateHost(); } /** * Returns the {@code Group} that positions and sizes components * along the vertical axis. * * @return the {@code Group} responsible for positioning and * sizing component along the vertical axis */ private Group getVerticalGroup() { int index = 0; if (verticalGroup.springs.size() > 1) { index = 1; } return (Group)verticalGroup.springs.get(index); } /** * Wraps the user specified group in a sequential group. If * container gaps should be generated the necessary springs are * added. */ private Group createTopLevelGroup(Group specifiedGroup) { SequentialGroup group = createSequentialGroup(); if (getAutoCreateContainerGaps()) { group.addSpring(new ContainerAutoPreferredGapSpring()); group.addGroup(specifiedGroup); group.addSpring(new ContainerAutoPreferredGapSpring()); } else { group.addGroup(specifiedGroup); } return group; } /** * Creates and returns a {@code SequentialGroup}. * * @return a new {@code SequentialGroup} */ public SequentialGroup createSequentialGroup() { return new SequentialGroup(); } /** * Creates and returns a {@code ParallelGroup} with an alignment of * {@code Alignment.LEADING}. This is a cover method for the more * general {@code createParallelGroup(Alignment)} method. * * @return a new {@code ParallelGroup} * @see #createParallelGroup(Alignment) */ public ParallelGroup createParallelGroup() { return createParallelGroup(Alignment.LEADING); } /** * Creates and returns a {@code ParallelGroup} with the specified * alignment. This is a cover method for the more general {@code * createParallelGroup(Alignment,boolean)} method with {@code true} * supplied for the second argument. * * @param alignment the alignment for the elements of the group * @throws IllegalArgumentException if {@code alignment} is {@code null} * @return a new {@code ParallelGroup} * @see #createBaselineGroup * @see ParallelGroup */ public ParallelGroup createParallelGroup(Alignment alignment) { return createParallelGroup(alignment, true); } /** * Creates and returns a {@code ParallelGroup} with the specified * alignment and resize behavior. The {@code * alignment} argument specifies how children elements are * positioned that do not fill the group. For example, if a {@code * ParallelGroup} with an alignment of {@code TRAILING} is given * 100 and a child only needs 50, the child is * positioned at the position 50 (with a component orientation of * left-to-right). * <p> * Baseline alignment is only useful when used along the vertical * axis. A {@code ParallelGroup} created with a baseline alignment * along the horizontal axis is treated as {@code LEADING}. * <p> * Refer to {@link GroupLayout.ParallelGroup ParallelGroup} for details on * the behavior of baseline groups. * * @param alignment the alignment for the elements of the group * @param resizable {@code true} if the group is resizable; if the group * is not resizable the preferred size is used for the * minimum and maximum size of the group * @throws IllegalArgumentException if {@code alignment} is {@code null} * @return a new {@code ParallelGroup} * @see #createBaselineGroup * @see GroupLayout.ParallelGroup */ public ParallelGroup createParallelGroup(Alignment alignment, boolean resizable){ if (alignment == null) { throw new IllegalArgumentException("alignment must be non null"); } if (alignment == Alignment.BASELINE) { return new BaselineGroup(resizable); } return new ParallelGroup(alignment, resizable); } /** * Creates and returns a {@code ParallelGroup} that aligns it's * elements along the baseline. * * @param resizable whether the group is resizable * @param anchorBaselineToTop whether the baseline is anchored to * the top or bottom of the group * @see #createBaselineGroup * @see ParallelGroup */ public ParallelGroup createBaselineGroup(boolean resizable, boolean anchorBaselineToTop) { return new BaselineGroup(resizable, anchorBaselineToTop); } /** * Forces the specified components to have the same size * regardless of their preferred, minimum or maximum sizes. Components that * are linked are given the maximum of the preferred size of each of * the linked components. For example, if you link two components with * a preferred width of 10 and 20, both components are given a width of 20. * <p> * This can be used multiple times to force any number of * components to share the same size. * <p> * Linked Components are not be resizable. * * @param components the {@code Component}s that are to have the same size * @throws IllegalArgumentException if {@code components} is * {@code null}, or contains {@code null} * @see #linkSize(int,Component[]) */ public void linkSize(Component... components) { linkSize(SwingConstants.HORIZONTAL, components); linkSize(SwingConstants.VERTICAL, components); } /** * Forces the specified components to have the same size along the * specified axis regardless of their preferred, minimum or * maximum sizes. Components that are linked are given the maximum * of the preferred size of each of the linked components. For * example, if you link two components along the horizontal axis * and the preferred width is 10 and 20, both components are given * a width of 20. * <p> * This can be used multiple times to force any number of * components to share the same size. * <p> * Linked {@code Component}s are not be resizable. * * @param components the {@code Component}s that are to have the same size * @param axis the axis to link the size along; one of * {@code SwingConstants.HORIZONTAL} or * {@code SwingConstans.VERTICAL} * @throws IllegalArgumentException if {@code components} is * {@code null}, or contains {@code null}; or {@code axis} * is not {@code SwingConstants.HORIZONTAL} or * {@code SwingConstants.VERTICAL} */ public void linkSize(int axis, Component... components) { if (components == null) { throw new IllegalArgumentException("Components must be non-null"); } for (int counter = components.length - 1; counter >= 0; counter--) { Component c = components[counter]; if (components[counter] == null) { throw new IllegalArgumentException( "Components must be non-null"); } // Force the component to be added getComponentInfo(c); } int glAxis; if (axis == SwingConstants.HORIZONTAL) { glAxis = HORIZONTAL; } else if (axis == SwingConstants.VERTICAL) { glAxis = VERTICAL; } else { throw new IllegalArgumentException("Axis must be one of " + "SwingConstants.HORIZONTAL or SwingConstants.VERTICAL"); } LinkInfo master = getComponentInfo( components[components.length - 1]).getLinkInfo(glAxis); for (int counter = components.length - 2; counter >= 0; counter--) { master.add(getComponentInfo(components[counter])); } invalidateHost(); } /** * Replaces an existing component with a new one. * * @param existingComponent the component that should be removed * and replaced with {@code newComponent} * @param newComponent the component to put in * {@code existingComponent}'s place * @throws IllegalArgumentException if either of the components are * {@code null} or {@code existingComponent} is not being managed * by this layout manager */ public void replace(Component existingComponent, Component newComponent) { if (existingComponent == null || newComponent == null) { throw new IllegalArgumentException("Components must be non-null"); } // Make sure all the components have been registered, otherwise we may // not update the correct Springs. if (springsChanged) { registerComponents(horizontalGroup, HORIZONTAL); registerComponents(verticalGroup, VERTICAL); } ComponentInfo info = componentInfos.remove(existingComponent); if (info == null) { throw new IllegalArgumentException("Component must already exist"); } host.remove(existingComponent); if (newComponent.getParent() != host) { host.add(newComponent); } info.setComponent(newComponent); componentInfos.put(newComponent, info); invalidateHost(); } /** * Sets the {@code LayoutStyle} used to calculate the preferred * gaps between components. A value of {@code null} indicates the * shared instance of {@code LayoutStyle} should be used. * * @param layoutStyle the {@code LayoutStyle} to use * @see LayoutStyle */ public void setLayoutStyle(LayoutStyle layoutStyle) { this.layoutStyle = layoutStyle; invalidateHost(); } /** * Returns the {@code LayoutStyle} used for calculating the preferred * gap between components. This returns the value specified to * {@code setLayoutStyle}, which may be {@code null}. * * @return the {@code LayoutStyle} used for calculating the preferred * gap between components */ public LayoutStyle getLayoutStyle() { return layoutStyle; } private LayoutStyle getLayoutStyle0() { LayoutStyle layoutStyle = getLayoutStyle(); if (layoutStyle == null) { layoutStyle = LayoutStyle.getInstance(); } return layoutStyle; } private void invalidateHost() { if (host instanceof JComponent) { ((JComponent)host).revalidate(); } else { host.invalidate(); } host.repaint(); } // // LayoutManager // /** * Notification that a {@code Component} has been added to * the parent container. You should not invoke this method * directly, instead you should use one of the {@code Group} * methods to add a {@code Component}. * * @param name the string to be associated with the component * @param component the {@code Component} to be added */ public void addLayoutComponent(String name, Component component) { } /** * Notification that a {@code Component} has been removed from * the parent container. You should not invoke this method * directly, instead invoke {@code remove} on the parent * {@code Container}. * * @param component the component to be removed * @see java.awt.Component#remove */ public void removeLayoutComponent(Component component) { ComponentInfo info = componentInfos.remove(component); if (info != null) { info.dispose(); springsChanged = true; isValid = false; } } /** * Returns the preferred size for the specified container. * * @param parent the container to return the preferred size for * @return the preferred size for {@code parent} * @throws IllegalArgumentException if {@code parent} is not * the same {@code Container} this was created with * @throws IllegalStateException if any of the components added to * this layout are not in both a horizontal and vertical group * @see java.awt.Container#getPreferredSize */ public Dimension preferredLayoutSize(Container parent) { checkParent(parent); prepare(PREF_SIZE); return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL), verticalGroup.getPreferredSize(VERTICAL)); } /** * Returns the minimum size for the specified container. * * @param parent the container to return the size for * @return the minimum size for {@code parent} * @throws IllegalArgumentException if {@code parent} is not * the same {@code Container} that this was created with * @throws IllegalStateException if any of the components added to * this layout are not in both a horizontal and vertical group * @see java.awt.Container#getMinimumSize */ public Dimension minimumLayoutSize(Container parent) { checkParent(parent); prepare(MIN_SIZE); return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL), verticalGroup.getMinimumSize(VERTICAL)); } /** * Lays out the specified container. * * @param parent the container to be laid out * @throws IllegalStateException if any of the components added to * this layout are not in both a horizontal and vertical group */ public void layoutContainer(Container parent) { // Step 1: Prepare for layout. prepare(SPECIFIC_SIZE); Insets insets = parent.getInsets(); int width = parent.getWidth() - insets.left - insets.right; int height = parent.getHeight() - insets.top - insets.bottom; boolean ltr = isLeftToRight(); if (getAutoCreateGaps() || getAutoCreateContainerGaps() || hasPreferredPaddingSprings) { // Step 2: Calculate autopadding springs calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0, width); calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0, height); } // Step 3: set the size of the groups. horizontalGroup.setSize(HORIZONTAL, 0, width); verticalGroup.setSize(VERTICAL, 0, height); // Step 4: apply the size to the components. for (ComponentInfo info : componentInfos.values()) { info.setBounds(insets, width, ltr); } } // // LayoutManager2 // /** * Notification that a {@code Component} has been added to * the parent container. You should not invoke this method * directly, instead you should use one of the {@code Group} * methods to add a {@code Component}. * * @param component the component added * @param constraints description of where to place the component */ public void addLayoutComponent(Component component, Object constraints) { } /** * Returns the maximum size for the specified container. * * @param parent the container to return the size for * @return the maximum size for {@code parent} * @throws IllegalArgumentException if {@code parent} is not * the same {@code Container} that this was created with * @throws IllegalStateException if any of the components added to * this layout are not in both a horizontal and vertical group * @see java.awt.Container#getMaximumSize */ public Dimension maximumLayoutSize(Container parent) { checkParent(parent); prepare(MAX_SIZE); return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL), verticalGroup.getMaximumSize(VERTICAL)); } /** * Returns the alignment along the x axis. This specifies how * the component would like to be aligned relative to other * components. The value should be a number between 0 and 1 * where 0 represents alignment along the origin, 1 is aligned * the furthest away from the origin, 0.5 is centered, etc. * * @param parent the {@code Container} hosting this {@code LayoutManager} * @throws IllegalArgumentException if {@code parent} is not * the same {@code Container} that this was created with * @return the alignment; this implementation returns {@code .5} */ public float getLayoutAlignmentX(Container parent) { checkParent(parent); return .5f; } /** * Returns the alignment along the y axis. This specifies how * the component would like to be aligned relative to other * components. The value should be a number between 0 and 1 * where 0 represents alignment along the origin, 1 is aligned * the furthest away from the origin, 0.5 is centered, etc. * * @param parent the {@code Container} hosting this {@code LayoutManager} * @throws IllegalArgumentException if {@code parent} is not * the same {@code Container} that this was created with * @return alignment; this implementation returns {@code .5} */ public float getLayoutAlignmentY(Container parent) { checkParent(parent); return .5f; } /** * Invalidates the layout, indicating that if the layout manager * has cached information it should be discarded. * * @param parent the {@code Container} hosting this LayoutManager * @throws IllegalArgumentException if {@code parent} is not * the same {@code Container} that this was created with */ public void invalidateLayout(Container parent) { checkParent(parent); // invalidateLayout is called from Container.invalidate, which // does NOT grab the treelock. All other methods do. To make sure // there aren't any possible threading problems we grab the tree lock // here. synchronized(parent.getTreeLock()) { isValid = false; } } private void prepare(int sizeType) { boolean visChanged = false; // Step 1: If not-valid, clear springs and update visibility. if (!isValid) { isValid = true; horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET); verticalGroup.setSize(VERTICAL, UNSET, UNSET); for (ComponentInfo ci : componentInfos.values()) { if (ci.updateVisibility()) { visChanged = true; } ci.clearCachedSize(); } } // Step 2: Make sure components are bound to ComponentInfos if (springsChanged) { registerComponents(horizontalGroup, HORIZONTAL); registerComponents(verticalGroup, VERTICAL); } // Step 3: Adjust the autopadding. This removes existing // autopadding, then recalculates where it should go. if (springsChanged || visChanged) { checkComponents(); horizontalGroup.removeAutopadding(); verticalGroup.removeAutopadding(); if (getAutoCreateGaps()) { insertAutopadding(true); } else if (hasPreferredPaddingSprings || getAutoCreateContainerGaps()) { insertAutopadding(false); } springsChanged = false; } // Step 4: (for min/pref/max size calculations only) calculate the // autopadding. This invokes for unsetting the calculated values, then // recalculating them. // If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this // step will be done later on. if (sizeType != SPECIFIC_SIZE && (getAutoCreateGaps() || getAutoCreateContainerGaps() || hasPreferredPaddingSprings)) { calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0); calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0); } } private void calculateAutopadding(Group group, int axis, int sizeType, int origin, int size) { group.unsetAutopadding(); switch(sizeType) { case MIN_SIZE: size = group.getMinimumSize(axis); break; case PREF_SIZE: size = group.getPreferredSize(axis); break; case MAX_SIZE: size = group.getMaximumSize(axis); break; default: break; } group.setSize(axis, origin, size); group.calculateAutopadding(axis); } private void checkComponents() { for (ComponentInfo info : componentInfos.values()) { if (info.horizontalSpring == null) { throw new IllegalStateException(info.component + " is not attached to a horizontal group"); } if (info.verticalSpring == null) { throw new IllegalStateException(info.component + " is not attached to a vertical group"); } } } private void registerComponents(Group group, int axis) { List<Spring> springs = group.springs; for (int counter = springs.size() - 1; counter >= 0; counter--) { Spring spring = springs.get(counter); if (spring instanceof ComponentSpring) { ((ComponentSpring)spring).installIfNecessary(axis); } else if (spring instanceof Group) { registerComponents((Group)spring, axis); } } } private Dimension adjustSize(int width, int height) { Insets insets = host.getInsets(); return new Dimension(width + insets.left + insets.right, height + insets.top + insets.bottom); } private void checkParent(Container parent) { if (parent != host) { throw new IllegalArgumentException( "GroupLayout can only be used with one Container at a time"); } } /** * Returns the {@code ComponentInfo} for the specified Component, * creating one if necessary. */ private ComponentInfo getComponentInfo(Component component) { ComponentInfo info = componentInfos.get(component); if (info == null) { info = new ComponentInfo(component); componentInfos.put(component, info); if (component.getParent() != host) { host.add(component); } } return info; } /** * Adjusts the autopadding springs for the horizontal and vertical * groups. If {@code insert} is {@code true} this will insert auto padding * springs, otherwise this will only adjust the springs that * comprise auto preferred padding springs. */ private void insertAutopadding(boolean insert) { horizontalGroup.insertAutopadding(HORIZONTAL, new ArrayList<AutoPreferredGapSpring>(1), new ArrayList<AutoPreferredGapSpring>(1), new ArrayList<ComponentSpring>(1), new ArrayList<ComponentSpring>(1), insert); verticalGroup.insertAutopadding(VERTICAL, new ArrayList<AutoPreferredGapSpring>(1), new ArrayList<AutoPreferredGapSpring>(1), new ArrayList<ComponentSpring>(1), new ArrayList<ComponentSpring>(1), insert); } /** * Returns {@code true} if the two Components have a common ParallelGroup * ancestor along the particular axis. */ private boolean areParallelSiblings(Component source, Component target, int axis) { ComponentInfo sourceInfo = getComponentInfo(source); ComponentInfo targetInfo = getComponentInfo(target); Spring sourceSpring; Spring targetSpring; if (axis == HORIZONTAL) { sourceSpring = sourceInfo.horizontalSpring; targetSpring = targetInfo.horizontalSpring; } else { sourceSpring = sourceInfo.verticalSpring; targetSpring = targetInfo.verticalSpring; } Set<Spring> sourcePath = tmpParallelSet; sourcePath.clear(); Spring spring = sourceSpring.getParent(); while (spring != null) { sourcePath.add(spring); spring = spring.getParent(); } spring = targetSpring.getParent(); while (spring != null) { if (sourcePath.contains(spring)) { sourcePath.clear(); while (spring != null) { if (spring instanceof ParallelGroup) { return true; } spring = spring.getParent(); } return false; } spring = spring.getParent(); } sourcePath.clear(); return false; } private boolean isLeftToRight() { return host.getComponentOrientation().isLeftToRight(); } /** * Returns a string representation of this {@code GroupLayout}. * This method is intended to be used for debugging purposes, * and the content and format of the returned string may vary * between implementations. * * @return a string representation of this {@code GroupLayout} **/ public String toString() { if (springsChanged) { registerComponents(horizontalGroup, HORIZONTAL); registerComponents(verticalGroup, VERTICAL); } StringBuffer buffer = new StringBuffer(); buffer.append("HORIZONTAL\n"); createSpringDescription(buffer, horizontalGroup, " ", HORIZONTAL); buffer.append("\nVERTICAL\n"); createSpringDescription(buffer, verticalGroup, " ", VERTICAL); return buffer.toString(); } private void createSpringDescription(StringBuffer buffer, Spring spring, String indent, int axis) { String origin = ""; String padding = ""; if (spring instanceof ComponentSpring) { ComponentSpring cSpring = (ComponentSpring)spring; origin = Integer.toString(cSpring.getOrigin()) + " "; String name = cSpring.getComponent().getName(); if (name != null) { origin = "name=" + name + ", "; } } if (spring instanceof AutoPreferredGapSpring) { AutoPreferredGapSpring paddingSpring = (AutoPreferredGapSpring)spring; padding = ", userCreated=" + paddingSpring.getUserCreated() + ", matches=" + paddingSpring.getMatchDescription(); } buffer.append(indent + spring.getClass().getName() + " " + Integer.toHexString(spring.hashCode()) + " " + origin + ", size=" + spring.getSize() + ", alignment=" + spring.getAlignment() + " prefs=[" + spring.getMinimumSize(axis) + " " + spring.getPreferredSize(axis) + " " + spring.getMaximumSize(axis) + padding + "]\n"); if (spring instanceof Group) { List<Spring> springs = ((Group)spring).springs; indent += " "; for (int counter = 0; counter < springs.size(); counter++) { createSpringDescription(buffer, springs.get(counter), indent, axis); } } } /** * Spring consists of a range: min, pref and max, a value some where in * the middle of that, and a location. Spring caches the * min/max/pref. If the min/pref/max has internally changes, or needs * to be updated you must invoke clear. */ private abstract class Spring { private int size; private int min; private int max; private int pref; private Spring parent; private Alignment alignment; Spring() { min = pref = max = UNSET; } /** * Calculates and returns the minimum size. * * @param axis the axis of layout; one of HORIZONTAL or VERTICAL * @return the minimum size */ abstract int calculateMinimumSize(int axis); /** * Calculates and returns the preferred size. * * @param axis the axis of layout; one of HORIZONTAL or VERTICAL * @return the preferred size */ abstract int calculatePreferredSize(int axis); /** * Calculates and returns the minimum size. * * @param axis the axis of layout; one of HORIZONTAL or VERTICAL * @return the minimum size */ abstract int calculateMaximumSize(int axis); /** * Sets the parent of this Spring. */ void setParent(Spring parent) { this.parent = parent; } /** * Returns the parent of this spring. */ Spring getParent() { return parent; } // This is here purely as a convenience for ParallelGroup to avoid // having to track alignment separately. void setAlignment(Alignment alignment) { this.alignment = alignment; } /** * Alignment for this Spring, this may be null. */ Alignment getAlignment() { return alignment; } /** * Returns the minimum size. */ final int getMinimumSize(int axis) { if (min == UNSET) { min = constrain(calculateMinimumSize(axis)); } return min; } /** * Returns the preferred size. */ final int getPreferredSize(int axis) { if (pref == UNSET) { pref = constrain(calculatePreferredSize(axis)); } return pref; } /** * Returns the maximum size. */ final int getMaximumSize(int axis) { if (max == UNSET) { max = constrain(calculateMaximumSize(axis)); } return max; } /** * Sets the value and location of the spring. Subclasses * will want to invoke super, then do any additional sizing. * * @param axis HORIZONTAL or VERTICAL * @param origin of this Spring * @param size of the Spring. If size is UNSET, this invokes * clear. */ void setSize(int axis, int origin, int size) { this.size = size; if (size == UNSET) { unset(); } } /** * Resets the cached min/max/pref. */ void unset() { size = min = pref = max = UNSET; } /** * Returns the current size. */ int getSize() { return size; } int constrain(int value) { return Math.min(value, Short.MAX_VALUE); } int getBaseline() { return -1; } BaselineResizeBehavior getBaselineResizeBehavior() { return BaselineResizeBehavior.OTHER; } final boolean isResizable(int axis) { int min = getMinimumSize(axis); int pref = getPreferredSize(axis); return (min != pref || pref != getMaximumSize(axis)); } /** * Returns {@code true} if this spring will ALWAYS have a zero * size. This should NOT check the current size, rather it's * meant to quickly test if this Spring will always have a * zero size. * * @param treatAutopaddingAsZeroSized if {@code true}, auto padding * springs should be treated as having a size of {@code 0} * @return {@code true} if this spring will have a zero size, * {@code false} otherwise */ abstract boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized); } /** * {@code Group} provides the basis for the two types of * operations supported by {@code GroupLayout}: laying out * components one after another ({@link SequentialGroup SequentialGroup}) * or aligned ({@link ParallelGroup ParallelGroup}). {@code Group} and * its subclasses have no public constructor; to create one use * one of {@code createSequentialGroup} or * {@code createParallelGroup}. Additionally, taking a {@code Group} * created from one {@code GroupLayout} and using it with another * will produce undefined results. * <p> * Various methods in {@code Group} and its subclasses allow you * to explicitly specify the range. The arguments to these methods * can take two forms, either a value greater than or equal to 0, * or one of {@code DEFAULT_SIZE} or {@code PREFERRED_SIZE}. A * value greater than or equal to {@code 0} indicates a specific * size. {@code DEFAULT_SIZE} indicates the corresponding size * from the component should be used. For example, if {@code * DEFAULT_SIZE} is passed as the minimum size argument, the * minimum size is obtained from invoking {@code getMinimumSize} * on the component. Likewise, {@code PREFERRED_SIZE} indicates * the value from {@code getPreferredSize} should be used. * The following example adds {@code myComponent} to {@code group} * with specific values for the range. That is, the minimum is * explicitly specified as 100, preferred as 200, and maximum as * 300. * <pre> * group.addComponent(myComponent, 100, 200, 300); * </pre> * The following example adds {@code myComponent} to {@code group} using * a combination of the forms. The minimum size is forced to be the * same as the preferred size, the preferred size is determined by * using {@code myComponent.getPreferredSize} and the maximum is * determined by invoking {@code getMaximumSize} on the component. * <pre> * group.addComponent(myComponent, GroupLayout.PREFERRED_SIZE, * GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE); * </pre> * <p> * Unless otherwise specified all the methods of {@code Group} and * its subclasses that allow you to specify a range throw an * {@code IllegalArgumentException} if passed an invalid range. An * invalid range is one in which any of the values are < 0 and * not one of {@code PREFERRED_SIZE} or {@code DEFAULT_SIZE}, or * the following is not met (for specific values): {@code min} * <= {@code pref} <= {@code max}. * <p> * Similarly any methods that take a {@code Component} throw a * {@code IllegalArgumentException} if passed {@code null} and any methods * that take a {@code Group} throw an {@code NullPointerException} if * passed {@code null}. * * @see #createSequentialGroup * @see #createParallelGroup * @since 1.6 */ public abstract class Group extends Spring { // private int origin; // private int size; List<Spring> springs; Group() { springs = new ArrayList<Spring>(); } /** * Adds a {@code Group} to this {@code Group}. * * @param group the {@code Group} to add * @return this {@code Group} */ public Group addGroup(Group group) { return addSpring(group); } /** * Adds a {@code Component} to this {@code Group}. * * @param component the {@code Component} to add * @return this {@code Group} */ public Group addComponent(Component component) { return addComponent(component, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE); } /** * Adds a {@code Component} to this {@code Group} * with the specified size. * * @param component the {@code Component} to add * @param min the minimum size or one of {@code DEFAULT_SIZE} or * {@code PREFERRED_SIZE} * @param pref the preferred size or one of {@code DEFAULT_SIZE} or * {@code PREFERRED_SIZE} * @param max the maximum size or one of {@code DEFAULT_SIZE} or * {@code PREFERRED_SIZE} * @return this {@code Group} */ public Group addComponent(Component component, int min, int pref, int max) { return addSpring(new ComponentSpring(component, min, pref, max)); } /** * Adds a rigid gap to this {@code Group}. * * @param size the size of the gap * @return this {@code Group} * @throws IllegalArgumentException if {@code size} is less than * {@code 0} */ public Group addGap(int size) { return addGap(size, size, size); } /** * Adds a gap to this {@code Group} with the specified size. * * @param min the minimum size of the gap * @param pref the preferred size of the gap * @param max the maximum size of the gap * @throws IllegalArgumentException if any of the values are * less than {@code 0} * @return this {@code Group} */ public Group addGap(int min, int pref, int max) { return addSpring(new GapSpring(min, pref, max)); } Spring getSpring(int index) { return springs.get(index); } int indexOf(Spring spring) { return springs.indexOf(spring); } /** * Adds the Spring to the list of {@code Spring}s and returns * the receiver. */ Group addSpring(Spring spring) { springs.add(spring); spring.setParent(this); if (!(spring instanceof AutoPreferredGapSpring) || !((AutoPreferredGapSpring)spring).getUserCreated()) { springsChanged = true; } return this; } // // Spring methods // void setSize(int axis, int origin, int size) { super.setSize(axis, origin, size); if (size == UNSET) { for (int counter = springs.size() - 1; counter >= 0; counter--) { getSpring(counter).setSize(axis, origin, size); } } else { setValidSize(axis, origin, size); } } /** * This is invoked from {@code setSize} if passed a value * other than UNSET. */ abstract void setValidSize(int axis, int origin, int size); int calculateMinimumSize(int axis) { return calculateSize(axis, MIN_SIZE); } int calculatePreferredSize(int axis) { return calculateSize(axis, PREF_SIZE); } int calculateMaximumSize(int axis) { return calculateSize(axis, MAX_SIZE); } /** * Calculates the specified size. This is called from * one of the {@code getMinimumSize0}, * {@code getPreferredSize0} or * {@code getMaximumSize0} methods. This will invoke * to {@code operator} to combine the values. */ int calculateSize(int axis, int type) { int count = springs.size(); if (count == 0) { return 0; } if (count == 1) { return getSpringSize(getSpring(0), axis, type); } int size = constrain(operator(getSpringSize(getSpring(0), axis, type), getSpringSize(getSpring(1), axis, type))); for (int counter = 2; counter < count; counter++) { size = constrain(operator(size, getSpringSize( getSpring(counter), axis, type))); } return size; } int getSpringSize(Spring spring, int axis, int type) { switch(type) { case MIN_SIZE: return spring.getMinimumSize(axis); case PREF_SIZE: return spring.getPreferredSize(axis); case MAX_SIZE: return spring.getMaximumSize(axis); } assert false; return 0; } /** * Used to compute how the two values representing two springs * will be combined. For example, a group that layed things out * one after the next would return {@code a + b}. */ abstract int operator(int a, int b); // // Padding // /** * Adjusts the autopadding springs in this group and its children. * If {@code insert} is true this will insert auto padding * springs, otherwise this will only adjust the springs that * comprise auto preferred padding springs. * * @param axis the axis of the springs; HORIZONTAL or VERTICAL * @param leadingPadding List of AutopaddingSprings that occur before * this Group * @param trailingPadding any trailing autopadding springs are added * to this on exit * @param leading List of ComponentSprings that occur before this Group * @param trailing any trailing ComponentSpring are added to this * List * @param insert Whether or not to insert AutopaddingSprings or just * adjust any existing AutopaddingSprings. */ abstract void insertAutopadding(int axis, List<AutoPreferredGapSpring> leadingPadding, List<AutoPreferredGapSpring> trailingPadding, List<ComponentSpring> leading, List trailing, boolean insert); /** * Removes any AutopaddingSprings for this Group and its children. */ void removeAutopadding() { unset(); for (int counter = springs.size() - 1; counter >= 0; counter--) { Spring spring = springs.get(counter); if (spring instanceof AutoPreferredGapSpring) { if (((AutoPreferredGapSpring)spring).getUserCreated()) { ((AutoPreferredGapSpring)spring).reset(); } else { springs.remove(counter); } } else if (spring instanceof Group) { ((Group)spring).removeAutopadding(); } } } void unsetAutopadding() { // Clear cached pref/min/max. unset(); for (int counter = springs.size() - 1; counter >= 0; counter--) { Spring spring = springs.get(counter); if (spring instanceof AutoPreferredGapSpring) { spring.unset(); } else if (spring instanceof Group) { ((Group)spring).unsetAutopadding(); } } } void calculateAutopadding(int axis) { for (int counter = springs.size() - 1; counter >= 0; counter--) { Spring spring = springs.get(counter); if (spring instanceof AutoPreferredGapSpring) { // Force size to be reset. spring.unset(); ((AutoPreferredGapSpring)spring).calculatePadding(axis); } else if (spring instanceof Group) { ((Group)spring).calculateAutopadding(axis); } } // Clear cached pref/min/max. unset(); } @Override boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) { for (int i = springs.size() - 1; i >= 0; i--) { Spring spring = springs.get(i); if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) { return false; } } return true; } } /** * A {@code Group} that positions and sizes its elements * sequentially, one after another. This class has no public * constructor, use the {@code createSequentialGroup} method * to create one. * <p> * In order to align a {@code SequentialGroup} along the baseline * of a baseline aligned {@code ParallelGroup} you need to specify * which of the elements of the {@code SequentialGroup} is used to * determine the baseline. The element used to calculate the * baseline is specified using one of the {@code add} methods that * take a {@code boolean}. The last element added with a value of * {@code true} for {@code useAsBaseline} is used to calculate the * baseline. * * @see #createSequentialGroup * @since 1.6 */ public class SequentialGroup extends Group { private Spring baselineSpring; SequentialGroup() { } /** * {@inheritDoc} */ public SequentialGroup addGroup(Group group) { return (SequentialGroup)super.addGroup(group); } /** * Adds a {@code Group} to this {@code Group}. * * @param group the {@code Group} to add * @param useAsBaseline whether the specified {@code Group} should * be used to calculate the baseline for this {@code Group} * @return this {@code Group} */ public SequentialGroup addGroup(boolean useAsBaseline, Group group) { super.addGroup(group); if (useAsBaseline) { baselineSpring = group; } return this; } /** * {@inheritDoc} */ public SequentialGroup addComponent(Component component) { return (SequentialGroup)super.addComponent(component); } /** * Adds a {@code Component} to this {@code Group}. * * @param useAsBaseline whether the specified {@code Component} should * be used to calculate the baseline for this {@code Group} * @param component the {@code Component} to add * @return this {@code Group} */ public SequentialGroup addComponent(boolean useAsBaseline, Component component) { super.addComponent(component); if (useAsBaseline) { baselineSpring = springs.get(springs.size() - 1); } return this; } /** * {@inheritDoc} */ public SequentialGroup addComponent(Component component, int min, int pref, int max) { return (SequentialGroup)super.addComponent( component, min, pref, max); } /** * Adds a {@code Component} to this {@code Group} * with the specified size. * * @param useAsBaseline whether the specified {@code Component} should * be used to calculate the baseline for this {@code Group} * @param component the {@code Component} to add * @param min the minimum size or one of {@code DEFAULT_SIZE} or * {@code PREFERRED_SIZE} * @param pref the preferred size or one of {@code DEFAULT_SIZE} or * {@code PREFERRED_SIZE} * @param max the maximum size or one of {@code DEFAULT_SIZE} or * {@code PREFERRED_SIZE} * @return this {@code Group} */ public SequentialGroup addComponent(boolean useAsBaseline, Component component, int min, int pref, int max) { super.addComponent(component, min, pref, max); if (useAsBaseline) { baselineSpring = springs.get(springs.size() - 1); } return this; } /** * {@inheritDoc} */ public SequentialGroup addGap(int size) { return (SequentialGroup)super.addGap(size); } /** * {@inheritDoc} */ public SequentialGroup addGap(int min, int pref, int max) { return (SequentialGroup)super.addGap(min, pref, max); } /** * Adds an element representing the preferred gap between two * components. The element created to represent the gap is not * resizable. * * @param comp1 the first component * @param comp2 the second component * @param type the type of gap; one of the constants defined by * {@code LayoutStyle} * @return this {@code SequentialGroup} * @throws IllegalArgumentException if {@code type}, {@code comp1} or * {@code comp2} is {@code null} * @see LayoutStyle */ public SequentialGroup addPreferredGap(JComponent comp1, JComponent comp2, ComponentPlacement type) { return addPreferredGap(comp1, comp2, type, DEFAULT_SIZE, PREFERRED_SIZE); } /** * Adds an element representing the preferred gap between two * components. * * @param comp1 the first component * @param comp2 the second component * @param type the type of gap * @param pref the preferred size of the grap; one of * {@code DEFAULT_SIZE} or a value >= 0 * @param max the maximum size of the gap; one of * {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE} * or a value >= 0 * @return this {@code SequentialGroup} * @throws IllegalArgumentException if {@code type}, {@code comp1} or * {@code comp2} is {@code null} * @see LayoutStyle */ public SequentialGroup addPreferredGap(JComponent comp1, JComponent comp2, ComponentPlacement type, int pref, int max) { if (type == null) { throw new IllegalArgumentException("Type must be non-null"); } if (comp1 == null || comp2 == null) { throw new IllegalArgumentException( "Components must be non-null"); } checkPreferredGapValues(pref, max); return (SequentialGroup)addSpring(new PreferredGapSpring( comp1, comp2, type, pref, max)); } /** * Adds an element representing the preferred gap between the * nearest components. During layout, neighboring * components are found, and the size of the added gap is set * based on the preferred gap between the components. If no * neighboring components are found the gap has a size of {@code 0}. * <p> * The element created to represent the gap is not * resizable. * * @param type the type of gap; one of * {@code LayoutStyle.ComponentPlacement.RELATED} or * {@code LayoutStyle.ComponentPlacement.UNRELATED} * @return this {@code SequentialGroup} * @see LayoutStyle * @throws IllegalArgumentException if {@code type} is not one of * {@code LayoutStyle.ComponentPlacement.RELATED} or * {@code LayoutStyle.ComponentPlacement.UNRELATED} */ public SequentialGroup addPreferredGap(ComponentPlacement type) { return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE); } /** * Adds an element representing the preferred gap between the * nearest components. During layout, neighboring * components are found, and the minimum of this * gap is set based on the size of the preferred gap between the * neighboring components. If no neighboring components are found the * minimum size is set to 0. * * @param type the type of gap; one of * {@code LayoutStyle.ComponentPlacement.RELATED} or * {@code LayoutStyle.ComponentPlacement.UNRELATED} * @param pref the preferred size of the grap; one of * {@code DEFAULT_SIZE} or a value >= 0 * @param max the maximum size of the gap; one of * {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE} * or a value >= 0 * @return this {@code SequentialGroup} * @throws IllegalArgumentException if {@code type} is not one of * {@code LayoutStyle.ComponentPlacement.RELATED} or * {@code LayoutStyle.ComponentPlacement.UNRELATED} * @see LayoutStyle */ public SequentialGroup addPreferredGap(ComponentPlacement type, int pref, int max) { if (type != ComponentPlacement.RELATED && type != ComponentPlacement.UNRELATED) { throw new IllegalArgumentException( "Type must be one of " + "LayoutStyle.ComponentPlacement.RELATED or " + "LayoutStyle.ComponentPlacement.UNRELATED"); } checkPreferredGapValues(pref, max); hasPreferredPaddingSprings = true; return (SequentialGroup)addSpring(new AutoPreferredGapSpring( type, pref, max)); } /** * Adds an element representing the preferred gap between an edge * the container and components that touch the border of the * container. This has no effect if the added gap does not * touch an edge of the parent container. * <p> * The element created to represent the gap is not * resizable. * * @return this {@code SequentialGroup} */ public SequentialGroup addContainerGap() { return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE); } /** * Adds an element representing the preferred gap between one * edge of the container and the next or previous {@code * Component} with the specified size. This has no * effect if the next or previous element is not a {@code * Component} and does not touch one edge of the parent * container. * * @param pref the preferred size; one of {@code DEFAULT_SIZE} or a * value >= 0 * @param max the maximum size; one of {@code DEFAULT_SIZE}, * {@code PREFERRED_SIZE} or a value >= 0 * @return this {@code SequentialGroup} */ public SequentialGroup addContainerGap(int pref, int max) { if ((pref < 0 && pref != DEFAULT_SIZE) || (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)|| (pref >= 0 && max >= 0 && pref > max)) { throw new IllegalArgumentException( "Pref and max must be either DEFAULT_VALUE " + "or >= 0 and pref <= max"); } hasPreferredPaddingSprings = true; return (SequentialGroup)addSpring( new ContainerAutoPreferredGapSpring(pref, max)); } int operator(int a, int b) { return constrain(a) + constrain(b); } void setValidSize(int axis, int origin, int size) { int pref = getPreferredSize(axis); if (size == pref) { // Layout at preferred size for (Spring spring : springs) { int springPref = spring.getPreferredSize(axis); spring.setSize(axis, origin, springPref); origin += springPref; } } else if (springs.size() == 1) { Spring spring = getSpring(0); spring.setSize(axis, origin, Math.min( Math.max(size, spring.getMinimumSize(axis)), spring.getMaximumSize(axis))); } else if (springs.size() > 1) { // Adjust between min/pref setValidSizeNotPreferred(axis, origin, size); } } private void setValidSizeNotPreferred(int axis, int origin, int size) { int delta = size - getPreferredSize(axis); assert delta != 0; boolean useMin = (delta < 0); int springCount = springs.size(); if (useMin) { delta *= -1; } // The following algorithm if used for resizing springs: // 1. Calculate the resizability of each spring (pref - min or // max - pref) into a list. // 2. Sort the list in ascending order // 3. Iterate through each of the resizable Springs, attempting // to give them (pref - size) / resizeCount // 4. For any Springs that can not accommodate that much space // add the remainder back to the amount to distribute and // recalculate how must space the remaining springs will get. // 5. Set the size of the springs. // First pass, sort the resizable springs into the List resizable List<SpringDelta> resizable = buildResizableList(axis, useMin); int resizableCount = resizable.size(); if (resizableCount > 0) { // How much we would like to give each Spring. int sDelta = delta / resizableCount; // Remaining space. int slop = delta - sDelta * resizableCount; int[] sizes = new int[springCount]; int sign = useMin ? -1 : 1; // Second pass, accumulate the resulting deltas (relative to // preferred) into sizes. for (int counter = 0; counter < resizableCount; counter++) { SpringDelta springDelta = resizable.get(counter); if ((counter + 1) == resizableCount) { sDelta += slop; } springDelta.delta = Math.min(sDelta, springDelta.delta); delta -= springDelta.delta; if (springDelta.delta != sDelta && counter + 1 < resizableCount) { // Spring didn't take all the space, reset how much // each spring will get. sDelta = delta / (resizableCount - counter - 1); slop = delta - sDelta * (resizableCount - counter - 1); } sizes[springDelta.index] = sign * springDelta.delta; } // And finally set the size of each spring for (int counter = 0; counter < springCount; counter++) { Spring spring = getSpring(counter); int sSize = spring.getPreferredSize(axis) + sizes[counter]; spring.setSize(axis, origin, sSize); origin += sSize; } } else { // Nothing resizable, use the min or max of each of the // springs. for (int counter = 0; counter < springCount; counter++) { Spring spring = getSpring(counter); int sSize; if (useMin) { sSize = spring.getMinimumSize(axis); } else { sSize = spring.getMaximumSize(axis); } spring.setSize(axis, origin, sSize); origin += sSize; } } } /** * Returns the sorted list of SpringDelta's for the current set of * Springs. The list is ordered based on the amount of flexibility of * the springs. */ private List<SpringDelta> buildResizableList(int axis, boolean useMin) { // First pass, figure out what is resizable int size = springs.size(); List<SpringDelta> sorted = new ArrayList(size); for (int counter = 0; counter < size; counter++) { Spring spring = getSpring(counter); int sDelta; if (useMin) { sDelta = spring.getPreferredSize(axis) - spring.getMinimumSize(axis); } else { sDelta = spring.getMaximumSize(axis) - spring.getPreferredSize(axis); } if (sDelta > 0) { sorted.add(new SpringDelta(counter, sDelta)); } } Collections.sort(sorted); return sorted; } private int indexOfNextNonZeroSpring( int index, boolean treatAutopaddingAsZeroSized) { while (index < springs.size()) { Spring spring = springs.get(index); if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) { return index; } index++; } return index; } @Override void insertAutopadding(int axis, List<AutoPreferredGapSpring> leadingPadding, List<AutoPreferredGapSpring> trailingPadding, List<ComponentSpring> leading, List trailing, boolean insert) { List<AutoPreferredGapSpring> newLeadingPadding = new ArrayList<AutoPreferredGapSpring>(leadingPadding); List<AutoPreferredGapSpring> newTrailingPadding = new ArrayList<AutoPreferredGapSpring>(1); List<ComponentSpring> newLeading = new ArrayList<ComponentSpring>(leading); List<ComponentSpring> newTrailing = null; int counter = 0; // Warning, this must use springs.size, as it may change during the // loop. while (counter < springs.size()) { Spring spring = getSpring(counter); if (spring instanceof AutoPreferredGapSpring) { if (newLeadingPadding.size() == 0) { // Autopadding spring. Set the sources of the // autopadding spring based on newLeading. AutoPreferredGapSpring padding = (AutoPreferredGapSpring)spring; padding.setSources(newLeading); newLeading.clear(); counter = indexOfNextNonZeroSpring(counter + 1, true); if (counter == springs.size()) { // Last spring in the list, add it to // trailingPadding. if (!(padding instanceof ContainerAutoPreferredGapSpring)) { trailingPadding.add(padding); } } else { newLeadingPadding.clear(); newLeadingPadding.add(padding); } } else { counter = indexOfNextNonZeroSpring(counter + 1, true); } } else { // Not a padding spring if (newLeading.size() > 0 && insert) { // There's leading ComponentSprings, create an // autopadding spring. AutoPreferredGapSpring padding = new AutoPreferredGapSpring(); // Force the newly created spring to be considered // by NOT incrementing counter springs.add(counter, padding); continue; } if (spring instanceof ComponentSpring) { // Spring is a Component, make it the target of any // leading AutopaddingSpring. ComponentSpring cSpring = (ComponentSpring)spring; if (!cSpring.isVisible()) { counter++; continue; } for (AutoPreferredGapSpring gapSpring : newLeadingPadding) { gapSpring.addTarget(cSpring, axis); } newLeading.clear(); newLeadingPadding.clear(); counter = indexOfNextNonZeroSpring(counter + 1, false); if (counter == springs.size()) { // Last Spring, add it to trailing trailing.add(cSpring); } else { // Not that last Spring, add it to leading newLeading.add(cSpring); } } else if (spring instanceof Group) { // Forward call to child Group if (newTrailing == null) { newTrailing = new ArrayList<ComponentSpring>(1); } else { newTrailing.clear(); } newTrailingPadding.clear(); ((Group)spring).insertAutopadding(axis, newLeadingPadding, newTrailingPadding, newLeading, newTrailing, insert); newLeading.clear(); newLeadingPadding.clear(); counter = indexOfNextNonZeroSpring( counter + 1, (newTrailing.size() == 0)); if (counter == springs.size()) { trailing.addAll(newTrailing); trailingPadding.addAll(newTrailingPadding); } else { newLeading.addAll(newTrailing); newLeadingPadding.addAll(newTrailingPadding); } } else { // Gap newLeadingPadding.clear(); newLeading.clear(); counter++; } } } } int getBaseline() { if (baselineSpring != null) { int baseline = baselineSpring.getBaseline(); if (baseline >= 0) { int size = 0; for (Spring spring : springs) { if (spring == baselineSpring) { return size + baseline; } else { size += spring.getPreferredSize(VERTICAL); } } } } return -1; } BaselineResizeBehavior getBaselineResizeBehavior() { if (isResizable(VERTICAL)) { if (!baselineSpring.isResizable(VERTICAL)) { // Spring to use for baseline isn't resizable. In this case // baseline resize behavior can be determined based on how // preceding springs resize. boolean leadingResizable = false; for (Spring spring : springs) { if (spring == baselineSpring) { break; } else if (spring.isResizable(VERTICAL)) { leadingResizable = true; break; } } boolean trailingResizable = false; for (int i = springs.size() - 1; i >= 0; i--) { Spring spring = springs.get(i); if (spring == baselineSpring) { break; } if (spring.isResizable(VERTICAL)) { trailingResizable = true; break; } } if (leadingResizable && !trailingResizable) { return BaselineResizeBehavior.CONSTANT_DESCENT; } else if (!leadingResizable && trailingResizable) { return BaselineResizeBehavior.CONSTANT_ASCENT; } // If we get here, both leading and trailing springs are // resizable. Fall through to OTHER. } else { BaselineResizeBehavior brb = baselineSpring.getBaselineResizeBehavior(); if (brb == BaselineResizeBehavior.CONSTANT_ASCENT) { for (Spring spring : springs) { if (spring == baselineSpring) { return BaselineResizeBehavior.CONSTANT_ASCENT; } if (spring.isResizable(VERTICAL)) { return BaselineResizeBehavior.OTHER; } } } else if (brb == BaselineResizeBehavior.CONSTANT_DESCENT) { for (int i = springs.size() - 1; i >= 0; i--) { Spring spring = springs.get(i); if (spring == baselineSpring) { return BaselineResizeBehavior.CONSTANT_DESCENT; } if (spring.isResizable(VERTICAL)) { return BaselineResizeBehavior.OTHER; } } } } return BaselineResizeBehavior.OTHER; } // Not resizable, treat as constant_ascent return BaselineResizeBehavior.CONSTANT_ASCENT; } private void checkPreferredGapValues(int pref, int max) { if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) || (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)|| (pref >= 0 && max >= 0 && pref > max)) { throw new IllegalArgumentException( "Pref and max must be either DEFAULT_SIZE, " + "PREFERRED_SIZE, or >= 0 and pref <= max"); } } } /** * Used by SequentialGroup in calculating resizability of springs. */ private static final class SpringDelta implements Comparable<SpringDelta> { // Original index. public final int index; // Delta, one of pref - min or max - pref. public int delta; public SpringDelta(int index, int delta) { this.index = index; this.delta = delta; } public int compareTo(SpringDelta o) { return delta - o.delta; } public String toString() { return super.toString() + "[index=" + index + ", delta=" + delta + "]"; } } /** * A {@code Group} that aligns and sizes it's children. * {@code ParallelGroup} aligns it's children in * four possible ways: along the baseline, centered, anchored to the * leading edge, or anchored to the trailing edge. * <h3>Baseline * A {@code ParallelGroup} that aligns it's children along the * baseline must first decide where the baseline is * anchored. The baseline can either be anchored to the top, or * anchored to the bottom of the group. That is, the distance between the * baseline and the beginning of the group can be a constant * distance, or the distance between the end of the group and the * baseline can be a constant distance. The possible choices * correspond to the {@code BaselineResizeBehavior} constants * {@link * java.awt.Component.BaselineResizeBehavior#CONSTANT_ASCENT CONSTANT_ASCENT} and * {@link * java.awt.Component.BaselineResizeBehavior#CONSTANT_DESCENT CONSTANT_DESCENT}. * <p> * The baseline anchor may be explicitly specified by the * {@code createBaselineGroup} method, or determined based on the elements. * If not explicitly specified, the baseline will be anchored to * the bottom if all the elements with a baseline, and that are * aligned to the baseline, have a baseline resize behavior of * {@code CONSTANT_DESCENT}; otherwise the baseline is anchored to the top * of the group. * <p> * Elements aligned to the baseline are resizable if they have have * a baseline resize behavior of {@code CONSTANT_ASCENT} or * {@code CONSTANT_DESCENT}. Elements with a baseline resize * behavior of {@code OTHER} or {@code CENTER_OFFSET} are not resizable. * <p> * The baseline is calculated based on the preferred height of each * of the elements that have a baseline. The baseline is * calculated using the following algorithm: * {@code max(maxNonBaselineHeight, maxAscent + maxDescent)}, where the * {@code maxNonBaselineHeight} is the maximum height of all elements * that do not have a baseline, or are not aligned along the baseline. * {@code maxAscent} is the maximum ascent (baseline) of all elements that * have a baseline and are aligned along the baseline. * {@code maxDescent} is the maximum descent (preferred height - baseline) * of all elements that have a baseline and are aligned along the baseline. * <p> * A {@code ParallelGroup} that aligns it's elements along the baseline * is only useful along the vertical axis. If you create a * baseline group and use it along the horizontal axis an * {@code IllegalStateException} is thrown when you ask * {@code GroupLayout} for the minimum, preferred or maximum size or * attempt to layout the components. * <p> * Elements that are not aligned to the baseline and smaller than the size * of the {@code ParallelGroup} are positioned in one of three * ways: centered, anchored to the leading edge, or anchored to the * trailing edge. * * <h3>Non-baseline {@code ParallelGroup} * {@code ParallelGroup}s created with an alignment other than * {@code BASELINE} align elements that are smaller than the size * of the group in one of three ways: centered, anchored to the * leading edge, or anchored to the trailing edge. * <p> * The leading edge is based on the axis and {@code * ComponentOrientation}. For the vertical axis the top edge is * always the leading edge, and the bottom edge is always the * trailing edge. When the {@code ComponentOrientation} is {@code * LEFT_TO_RIGHT}, the leading edge is the left edge and the * trailing edge the right edge. A {@code ComponentOrientation} of * {@code RIGHT_TO_LEFT} flips the left and right edges. Child * elements are aligned based on the specified alignment the * element was added with. If you do not specify an alignment, the * alignment specified for the {@code ParallelGroup} is used. * <p> * To align elements along the baseline you {@code createBaselineGroup}, * or {@code createParallelGroup} with an alignment of {@code BASELINE}. * If the group was not created with a baseline alignment, and you attempt * to add an element specifying a baseline alignment, an * {@code IllegalArgumentException} is thrown. * * @see #createParallelGroup() * @see #createBaselineGroup(boolean,boolean) * @since 1.6 */ public class ParallelGroup extends Group { // How children are layed out. private final Alignment childAlignment; // Whether or not we're resizable. private final boolean resizable; ParallelGroup(Alignment childAlignment, boolean resizable) { this.childAlignment = childAlignment; this.resizable = resizable; } /** * {@inheritDoc} */ public ParallelGroup addGroup(Group group) { return (ParallelGroup)super.addGroup(group); } /** * {@inheritDoc} */ public ParallelGroup addComponent(Component component) { return (ParallelGroup)super.addComponent(component); } /** * {@inheritDoc} */ public ParallelGroup addComponent(Component component, int min, int pref, int max) { return (ParallelGroup)super.addComponent(component, min, pref, max); } /** * {@inheritDoc} */ public ParallelGroup addGap(int pref) { return (ParallelGroup)super.addGap(pref); } /** * {@inheritDoc} */ public ParallelGroup addGap(int min, int pref, int max) { return (ParallelGroup)super.addGap(min, pref, max); } /** * Adds a {@code Group} to this {@code ParallelGroup} with the * specified alignment. If the child is smaller than the * {@code Group} it is aligned based on the specified * alignment. * * @param alignment the alignment * @param group the {@code Group} to add * @return this {@code ParallelGroup} * @throws IllegalArgumentException if {@code alignment} is * {@code null} */ public ParallelGroup addGroup(Alignment alignment, Group group) { checkChildAlignment(alignment); group.setAlignment(alignment); return (ParallelGroup)addSpring(group); } /** * Adds a {@code Component} to this {@code ParallelGroup} with * the specified alignment. * * @param alignment the alignment * @param component the {@code Component} to add * @return this {@code Group} * @throws IllegalArgumentException if {@code alignment} is * {@code null} */ public ParallelGroup addComponent(Component component, Alignment alignment) { return addComponent(component, alignment, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE); } /** * Adds a {@code Component} to this {@code ParallelGroup} with the * specified alignment and size. * * @param alignment the alignment * @param component the {@code Component} to add * @param min the minimum size * @param pref the preferred size * @param max the maximum size * @throws IllegalArgumentException if {@code alignment} is * {@code null} * @return this {@code Group} */ public ParallelGroup addComponent(Component component, Alignment alignment, int min, int pref, int max) { checkChildAlignment(alignment); ComponentSpring spring = new ComponentSpring(component, min, pref, max); spring.setAlignment(alignment); return (ParallelGroup)addSpring(spring); } boolean isResizable() { return resizable; } int operator(int a, int b) { return Math.max(a, b); } int calculateMinimumSize(int axis) { if (!isResizable()) { return getPreferredSize(axis); } return super.calculateMinimumSize(axis); } int calculateMaximumSize(int axis) { if (!isResizable()) { return getPreferredSize(axis); } return super.calculateMaximumSize(axis); } void setValidSize(int axis, int origin, int size) { for (Spring spring : springs) { setChildSize(spring, axis, origin, size); } } void setChildSize(Spring spring, int axis, int origin, int size) { Alignment alignment = spring.getAlignment(); int springSize = Math.min( Math.max(spring.getMinimumSize(axis), size), spring.getMaximumSize(axis)); if (alignment == null) { alignment = childAlignment; } switch (alignment) { case TRAILING: spring.setSize(axis, origin + size - springSize, springSize); break; case CENTER: spring.setSize(axis, origin + (size - springSize) / 2,springSize); break; default: // LEADING, or BASELINE spring.setSize(axis, origin, springSize); break; } } @Override void insertAutopadding(int axis, List<AutoPreferredGapSpring> leadingPadding, List<AutoPreferredGapSpring> trailingPadding, List<ComponentSpring> leading, List trailing, boolean insert) { for (Spring spring : springs) { if (spring instanceof ComponentSpring) { if (((ComponentSpring)spring).isVisible()) { for (AutoPreferredGapSpring gapSpring : leadingPadding) { gapSpring.addTarget((ComponentSpring)spring, axis); } trailing.add((ComponentSpring)spring); } } else if (spring instanceof Group) { ((Group)spring).insertAutopadding(axis, leadingPadding, trailingPadding, leading, trailing, insert); } else if (spring instanceof AutoPreferredGapSpring) { ((AutoPreferredGapSpring)spring).setSources(leading); trailingPadding.add((AutoPreferredGapSpring)spring); } } } private void checkChildAlignment(Alignment alignment) { checkChildAlignment(alignment, (this instanceof BaselineGroup)); } private void checkChildAlignment(Alignment alignment, boolean allowsBaseline) { if (alignment == null) { throw new IllegalArgumentException("Alignment must be non-null"); } if (!allowsBaseline && alignment == Alignment.BASELINE) { throw new IllegalArgumentException("Alignment must be one of:" + "LEADING, TRAILING or CENTER"); } } } /** * An extension of {@code ParallelGroup} that aligns its * constituent {@code Spring}s along the baseline. */ private class BaselineGroup extends ParallelGroup { // Whether or not all child springs have a baseline private boolean allSpringsHaveBaseline; // max(spring.getBaseline()) of all springs aligned along the baseline // that have a baseline private int prefAscent; // max(spring.getPreferredSize().height - spring.getBaseline()) of all // springs aligned along the baseline that have a baseline private int prefDescent; // Whether baselineAnchoredToTop was explicitly set private boolean baselineAnchorSet; // Whether the baseline is anchored to the top or the bottom. // If anchored to the top the baseline is always at prefAscent, // otherwise the baseline is at (height - prefDescent) private boolean baselineAnchoredToTop; // Whether or not the baseline has been calculated. private boolean calcedBaseline; BaselineGroup(boolean resizable) { super(Alignment.LEADING, resizable); prefAscent = prefDescent = -1; calcedBaseline = false; } BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) { this(resizable); this.baselineAnchoredToTop = baselineAnchoredToTop; baselineAnchorSet = true; } void unset() { super.unset(); prefAscent = prefDescent = -1; calcedBaseline = false; } void setValidSize(int axis, int origin, int size) { checkAxis(axis); if (prefAscent == -1) { super.setValidSize(axis, origin, size); } else { // do baseline layout baselineLayout(origin, size); } } int calculateSize(int axis, int type) { checkAxis(axis); if (!calcedBaseline) { calculateBaselineAndResizeBehavior(); } if (type == MIN_SIZE) { return calculateMinSize(); } if (type == MAX_SIZE) { return calculateMaxSize(); } if (allSpringsHaveBaseline) { return prefAscent + prefDescent; } return Math.max(prefAscent + prefDescent, super.calculateSize(axis, type)); } private void calculateBaselineAndResizeBehavior() { // calculate baseline prefAscent = 0; prefDescent = 0; int baselineSpringCount = 0; BaselineResizeBehavior resizeBehavior = null; for (Spring spring : springs) { if (spring.getAlignment() == null || spring.getAlignment() == Alignment.BASELINE) { int baseline = spring.getBaseline(); if (baseline >= 0) { if (spring.isResizable(VERTICAL)) { BaselineResizeBehavior brb = spring. getBaselineResizeBehavior(); if (resizeBehavior == null) { resizeBehavior = brb; } else if (brb != resizeBehavior) { resizeBehavior = BaselineResizeBehavior. CONSTANT_ASCENT; } } prefAscent = Math.max(prefAscent, baseline); prefDescent = Math.max(prefDescent, spring. getPreferredSize(VERTICAL) - baseline); baselineSpringCount++; } } } if (!baselineAnchorSet) { if (resizeBehavior == BaselineResizeBehavior.CONSTANT_DESCENT){ this.baselineAnchoredToTop = false; } else { this.baselineAnchoredToTop = true; } } allSpringsHaveBaseline = (baselineSpringCount == springs.size()); calcedBaseline = true; } private int calculateMaxSize() { int maxAscent = prefAscent; int maxDescent = prefDescent; int nonBaselineMax = 0; for (Spring spring : springs) { int baseline; int springMax = spring.getMaximumSize(VERTICAL); if ((spring.getAlignment() == null || spring.getAlignment() == Alignment.BASELINE) && (baseline = spring.getBaseline()) >= 0) { int springPref = spring.getPreferredSize(VERTICAL); if (springPref != springMax) { switch (spring.getBaselineResizeBehavior()) { case CONSTANT_ASCENT: if (baselineAnchoredToTop) { maxDescent = Math.max(maxDescent, springMax - baseline); } break; case CONSTANT_DESCENT: if (!baselineAnchoredToTop) { maxAscent = Math.max(maxAscent, springMax - springPref + baseline); } break; default: // CENTER_OFFSET and OTHER, not resizable break; } } } else { // Not aligned along the baseline, or no baseline. nonBaselineMax = Math.max(nonBaselineMax, springMax); } } return Math.max(nonBaselineMax, maxAscent + maxDescent); } private int calculateMinSize() { int minAscent = 0; int minDescent = 0; int nonBaselineMin = 0; if (baselineAnchoredToTop) { minAscent = prefAscent; } else { minDescent = prefDescent; } for (Spring spring : springs) { int springMin = spring.getMinimumSize(VERTICAL); int baseline; if ((spring.getAlignment() == null || spring.getAlignment() == Alignment.BASELINE) && (baseline = spring.getBaseline()) >= 0) { int springPref = spring.getPreferredSize(VERTICAL); BaselineResizeBehavior brb = spring. getBaselineResizeBehavior(); switch (brb) { case CONSTANT_ASCENT: if (baselineAnchoredToTop) { minDescent = Math.max(springMin - baseline, minDescent); } else { minAscent = Math.max(baseline, minAscent); } break; case CONSTANT_DESCENT: if (!baselineAnchoredToTop) { minAscent = Math.max( baseline - (springPref - springMin), minAscent); } else { minDescent = Math.max(springPref - baseline, minDescent); } break; default: // CENTER_OFFSET and OTHER are !resizable, use // the preferred size. minAscent = Math.max(baseline, minAscent); minDescent = Math.max(springPref - baseline, minDescent); break; } } else { // Not aligned along the baseline, or no baseline. nonBaselineMin = Math.max(nonBaselineMin, springMin); } } return Math.max(nonBaselineMin, minAscent + minDescent); } /** * Lays out springs that have a baseline along the baseline. All * others are centered. */ private void baselineLayout(int origin, int size) { int ascent; int descent; if (baselineAnchoredToTop) { ascent = prefAscent; descent = size - ascent; } else { ascent = size - prefDescent; descent = prefDescent; } for (Spring spring : springs) { Alignment alignment = spring.getAlignment(); if (alignment == null || alignment == Alignment.BASELINE) { int baseline = spring.getBaseline(); if (baseline >= 0) { int springMax = spring.getMaximumSize(VERTICAL); int springPref = spring.getPreferredSize(VERTICAL); int height = springPref; int y; switch(spring.getBaselineResizeBehavior()) { case CONSTANT_ASCENT: y = origin + ascent - baseline; height = Math.min(descent, springMax - baseline) + baseline; break; case CONSTANT_DESCENT: height = Math.min(ascent, springMax - springPref + baseline) + (springPref - baseline); y = origin + ascent + (springPref - baseline) - height; break; default: // CENTER_OFFSET & OTHER, not resizable y = origin + ascent - baseline; break; } spring.setSize(VERTICAL, y, height); } else { setChildSize(spring, VERTICAL, origin, size); } } else { setChildSize(spring, VERTICAL, origin, size); } } } int getBaseline() { if (springs.size() > 1) { // Force the baseline to be calculated getPreferredSize(VERTICAL); return prefAscent; } else if (springs.size() == 1) { return springs.get(0).getBaseline(); } return -1; } BaselineResizeBehavior getBaselineResizeBehavior() { if (springs.size() == 1) { return springs.get(0).getBaselineResizeBehavior(); } if (baselineAnchoredToTop) { return BaselineResizeBehavior.CONSTANT_ASCENT; } return BaselineResizeBehavior.CONSTANT_DESCENT; } // If the axis is VERTICAL, throws an IllegalStateException private void checkAxis(int axis) { if (axis == HORIZONTAL) { throw new IllegalStateException( "Baseline must be used along vertical axis"); } } } private final class ComponentSpring extends Spring { private Component component; private int origin; // min/pref/max are either a value >= 0 or one of // DEFAULT_SIZE or PREFERRED_SIZE private final int min; private final int pref; private final int max; // Baseline for the component, computed as necessary. private int baseline = -1; // Whether or not the size has been requested yet. private boolean installed; private ComponentSpring(Component component, int min, int pref, int max) { this.component = component; if (component == null) { throw new IllegalArgumentException( "Component must be non-null"); } checkSize(min, pref, max, true); this.min = min; this.max = max; this.pref = pref; // getComponentInfo makes sure component is a child of the // Container GroupLayout is the LayoutManager for. getComponentInfo(component); } int calculateMinimumSize(int axis) { if (isLinked(axis)) { return getLinkSize(axis, MIN_SIZE); } return calculateNonlinkedMinimumSize(axis); } int calculatePreferredSize(int axis) { if (isLinked(axis)) { return getLinkSize(axis, PREF_SIZE); } int min = getMinimumSize(axis); int pref = calculateNonlinkedPreferredSize(axis); int max = getMaximumSize(axis); return Math.min(max, Math.max(min, pref)); } int calculateMaximumSize(int axis) { if (isLinked(axis)) { return getLinkSize(axis, MAX_SIZE); } return Math.max(getMinimumSize(axis), calculateNonlinkedMaximumSize(axis)); } boolean isVisible() { return getComponentInfo(getComponent()).isVisible(); } int calculateNonlinkedMinimumSize(int axis) { if (!isVisible()) { return 0; } if (min >= 0) { return min; } if (min == PREFERRED_SIZE) { return calculateNonlinkedPreferredSize(axis); } assert (min == DEFAULT_SIZE); return getSizeAlongAxis(axis, component.getMinimumSize()); } int calculateNonlinkedPreferredSize(int axis) { if (!isVisible()) { return 0; } if (pref >= 0) { return pref; } assert (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE); return getSizeAlongAxis(axis, component.getPreferredSize()); } int calculateNonlinkedMaximumSize(int axis) { if (!isVisible()) { return 0; } if (max >= 0) { return max; } if (max == PREFERRED_SIZE) { return calculateNonlinkedPreferredSize(axis); } assert (max == DEFAULT_SIZE); return getSizeAlongAxis(axis, component.getMaximumSize()); } private int getSizeAlongAxis(int axis, Dimension size) { return (axis == HORIZONTAL) ? size.width : size.height; } private int getLinkSize(int axis, int type) { if (!isVisible()) { return 0; } ComponentInfo ci = getComponentInfo(component); return ci.getLinkSize(axis, type); } void setSize(int axis, int origin, int size) { super.setSize(axis, origin, size); this.origin = origin; if (size == UNSET) { baseline = -1; } } int getOrigin() { return origin; } void setComponent(Component component) { this.component = component; } Component getComponent() { return component; } int getBaseline() { if (baseline == -1) { Spring horizontalSpring = getComponentInfo(component). horizontalSpring; int width = horizontalSpring.getPreferredSize(HORIZONTAL); int height = getPreferredSize(VERTICAL); if (width > 0 && height > 0) { baseline = component.getBaseline(width, height); } } return baseline; } BaselineResizeBehavior getBaselineResizeBehavior() { return getComponent().getBaselineResizeBehavior(); } private boolean isLinked(int axis) { return getComponentInfo(component).isLinked(axis); } void installIfNecessary(int axis) { if (!installed) { installed = true; if (axis == HORIZONTAL) { getComponentInfo(component).horizontalSpring = this; } else { getComponentInfo(component).verticalSpring = this; } } } @Override boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) { return !isVisible(); } } /** * Spring representing the preferred distance between two components. */ private class PreferredGapSpring extends Spring { private final JComponent source; private final JComponent target; private final ComponentPlacement type; private final int pref; private final int max; PreferredGapSpring(JComponent source, JComponent target, ComponentPlacement type, int pref, int max) { this.source = source; this.target = target; this.type = type; this.pref = pref; this.max = max; } int calculateMinimumSize(int axis) { return getPadding(axis); } int calculatePreferredSize(int axis) { if (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE) { return getMinimumSize(axis); } int min = getMinimumSize(axis); int max = getMaximumSize(axis); return Math.min(max, Math.max(min, pref)); } int calculateMaximumSize(int axis) { if (max == PREFERRED_SIZE || max == DEFAULT_SIZE) { return getPadding(axis); } return Math.max(getMinimumSize(axis), max); } private int getPadding(int axis) { int position; if (axis == HORIZONTAL) { position = SwingConstants.EAST; } else { position = SwingConstants.SOUTH; } return getLayoutStyle0().getPreferredGap(source, target, type, position, host); } @Override boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) { return false; } } /** * Spring represented a certain amount of space. */ private class GapSpring extends Spring { private final int min; private final int pref; private final int max; GapSpring(int min, int pref, int max) { checkSize(min, pref, max, false); this.min = min; this.pref = pref; this.max = max; } int calculateMinimumSize(int axis) { if (min == PREFERRED_SIZE) { return getPreferredSize(axis); } return min; } int calculatePreferredSize(int axis) { return pref; } int calculateMaximumSize(int axis) { if (max == PREFERRED_SIZE) { return getPreferredSize(axis); } return max; } @Override boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) { return false; } } /** * Spring reprensenting the distance between any number of sources and * targets. The targets and sources are computed during layout. An * instance of this can either be dynamically created when * autocreatePadding is true, or explicitly created by the developer. */ private class AutoPreferredGapSpring extends Spring { List<ComponentSpring> sources; ComponentSpring source; private List<AutoPreferredGapMatch> matches; int size; int lastSize; private final int pref; private final int max; // Type of gap private ComponentPlacement type; private boolean userCreated; private AutoPreferredGapSpring() { this.pref = PREFERRED_SIZE; this.max = PREFERRED_SIZE; this.type = ComponentPlacement.RELATED; } AutoPreferredGapSpring(int pref, int max) { this.pref = pref; this.max = max; } AutoPreferredGapSpring(ComponentPlacement type, int pref, int max) { this.type = type; this.pref = pref; this.max = max; this.userCreated = true; } public void setSource(ComponentSpring source) { this.source = source; } public void setSources(List<ComponentSpring> sources) { this.sources = new ArrayList<ComponentSpring>(sources); } public void setUserCreated(boolean userCreated) { this.userCreated = userCreated; } public boolean getUserCreated() { return userCreated; } void unset() { lastSize = getSize(); super.unset(); size = 0; } public void reset() { size = 0; sources = null; source = null; matches = null; } public void calculatePadding(int axis) { size = UNSET; int maxPadding = UNSET; if (matches != null) { LayoutStyle p = getLayoutStyle0(); int position; if (axis == HORIZONTAL) { if (isLeftToRight()) { position = SwingConstants.EAST; } else { position = SwingConstants.WEST; } } else { position = SwingConstants.SOUTH; } for (int i = matches.size() - 1; i >= 0; i--) { AutoPreferredGapMatch match = matches.get(i); maxPadding = Math.max(maxPadding, calculatePadding(p, position, match.source, match.target)); } } if (size == UNSET) { size = 0; } if (maxPadding == UNSET) { maxPadding = 0; } if (lastSize != UNSET) { size += Math.min(maxPadding, lastSize); } } private int calculatePadding(LayoutStyle p, int position, ComponentSpring source, ComponentSpring target) { int delta = target.getOrigin() - (source.getOrigin() + source.getSize()); if (delta >= 0) { int padding; if ((source.getComponent() instanceof JComponent) && (target.getComponent() instanceof JComponent)) { padding = p.getPreferredGap( (JComponent)source.getComponent(), (JComponent)target.getComponent(), type, position, host); } else { padding = 10; } if (padding > delta) { size = Math.max(size, padding - delta); } return padding; } return 0; } public void addTarget(ComponentSpring spring, int axis) { int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL; if (source != null) { if (areParallelSiblings(source.getComponent(), spring.getComponent(), oAxis)) { addValidTarget(source, spring); } } else { Component component = spring.getComponent(); for (int counter = sources.size() - 1; counter >= 0; counter--){ ComponentSpring source = sources.get(counter); if (areParallelSiblings(source.getComponent(), component, oAxis)) { addValidTarget(source, spring); } } } } private void addValidTarget(ComponentSpring source, ComponentSpring target) { if (matches == null) { matches = new ArrayList<AutoPreferredGapMatch>(1); } matches.add(new AutoPreferredGapMatch(source, target)); } int calculateMinimumSize(int axis) { return size; } int calculatePreferredSize(int axis) { if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) { return size; } return Math.max(size, pref); } int calculateMaximumSize(int axis) { if (max >= 0) { return Math.max(getPreferredSize(axis), max); } return size; } String getMatchDescription() { return (matches == null) ? "" : matches.toString(); } public String toString() { return super.toString() + getMatchDescription(); } @Override boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) { return treatAutopaddingAsZeroSized; } } /** * Represents two springs that should have autopadding inserted between * them. */ private final static class AutoPreferredGapMatch { public final ComponentSpring source; public final ComponentSpring target; AutoPreferredGapMatch(ComponentSpring source, ComponentSpring target) { this.source = source; this.target = target; } private String toString(ComponentSpring spring) { return spring.getComponent().getName(); } public String toString() { return "[" + toString(source) + "-" + toString(target) + "]"; } } /** * An extension of AutopaddingSpring used for container level padding. */ private class ContainerAutoPreferredGapSpring extends AutoPreferredGapSpring { private List<ComponentSpring> targets; ContainerAutoPreferredGapSpring() { super(); setUserCreated(true); } ContainerAutoPreferredGapSpring(int pref, int max) { super(pref, max); setUserCreated(true); } public void addTarget(ComponentSpring spring, int axis) { if (targets == null) { targets = new ArrayList<ComponentSpring>(1); } targets.add(spring); } public void calculatePadding(int axis) { LayoutStyle p = getLayoutStyle0(); int maxPadding = 0; int position; size = 0; if (targets != null) { // Leading if (axis == HORIZONTAL) { if (isLeftToRight()) { position = SwingConstants.WEST; } else { position = SwingConstants.EAST; } } else { position = SwingConstants.SOUTH; } for (int i = targets.size() - 1; i >= 0; i--) { ComponentSpring targetSpring = targets.get(i); int padding = 10; if (targetSpring.getComponent() instanceof JComponent) { padding = p.getContainerGap( (JComponent)targetSpring.getComponent(), position, host); maxPadding = Math.max(padding, maxPadding); padding -= targetSpring.getOrigin(); } else { maxPadding = Math.max(padding, maxPadding); } size = Math.max(size, padding); } } else { // Trailing if (axis == HORIZONTAL) { if (isLeftToRight()) { position = SwingConstants.EAST; } else { position = SwingConstants.WEST; } } else { position = SwingConstants.SOUTH; } if (sources != null) { for (int i = sources.size() - 1; i >= 0; i--) { ComponentSpring sourceSpring = sources.get(i); maxPadding = Math.max(maxPadding, updateSize(p, sourceSpring, position)); } } else if (source != null) { maxPadding = updateSize(p, source, position); } } if (lastSize != UNSET) { size += Math.min(maxPadding, lastSize); } } private int updateSize(LayoutStyle p, ComponentSpring sourceSpring, int position) { int padding = 10; if (sourceSpring.getComponent() instanceof JComponent) { padding = p.getContainerGap( (JComponent)sourceSpring.getComponent(), position, host); } int delta = Math.max(0, getParent().getSize() - sourceSpring.getSize() - sourceSpring.getOrigin()); size = Math.max(size, padding - delta); return padding; } String getMatchDescription() { if (targets != null) { return "leading: " + targets.toString(); } if (sources != null) { return "trailing: " + sources.toString(); } return "--"; } } // LinkInfo contains the set of ComponentInfosthat are linked along a // particular axis. private static class LinkInfo { private final int axis; private final List<ComponentInfo> linked; private int size; LinkInfo(int axis) { linked = new ArrayList<ComponentInfo>(); size = UNSET; this.axis = axis; } public void add(ComponentInfo child) { LinkInfo childMaster = child.getLinkInfo(axis, false); if (childMaster == null) { linked.add(child); child.setLinkInfo(axis, this); } else if (childMaster != this) { linked.addAll(childMaster.linked); for (ComponentInfo childInfo : childMaster.linked) { childInfo.setLinkInfo(axis, this); } } clearCachedSize(); } public void remove(ComponentInfo info) { linked.remove(info); info.setLinkInfo(axis, null); if (linked.size() == 1) { linked.get(0).setLinkInfo(axis, null); } clearCachedSize(); } public void clearCachedSize() { size = UNSET; } public int getSize(int axis) { if (size == UNSET) { size = calculateLinkedSize(axis); } return size; } private int calculateLinkedSize(int axis) { int size = 0; for (ComponentInfo info : linked) { ComponentSpring spring; if (axis == HORIZONTAL) { spring = info.horizontalSpring; } else { assert (axis == VERTICAL); spring = info.verticalSpring; } size = Math.max(size, spring.calculateNonlinkedPreferredSize(axis)); } return size; } } /** * Tracks the horizontal/vertical Springs for a Component. * This class is also used to handle Springs that have their sizes * linked. */ private class ComponentInfo { // Component being layed out private Component component; ComponentSpring horizontalSpring; ComponentSpring verticalSpring; // If the component's size is linked to other components, the // horizontalMaster and/or verticalMaster reference the group of // linked components. private LinkInfo horizontalMaster; private LinkInfo verticalMaster; private boolean visible; private Boolean honorsVisibility; ComponentInfo(Component component) { this.component = component; updateVisibility(); } public void dispose() { // Remove horizontal/vertical springs removeSpring(horizontalSpring); horizontalSpring = null; removeSpring(verticalSpring); verticalSpring = null; // Clean up links if (horizontalMaster != null) { horizontalMaster.remove(this); } if (verticalMaster != null) { verticalMaster.remove(this); } } void setHonorsVisibility(Boolean honorsVisibility) { this.honorsVisibility = honorsVisibility; } private void removeSpring(Spring spring) { if (spring != null) { ((Group)spring.getParent()).springs.remove(spring); } } public boolean isVisible() { return visible; } /** * Updates the cached visibility. * * @return true if the visibility changed */ boolean updateVisibility() { boolean honorsVisibility; if (this.honorsVisibility == null) { honorsVisibility = GroupLayout.this.getHonorsVisibility(); } else { honorsVisibility = this.honorsVisibility; } boolean newVisible = (honorsVisibility) ? component.isVisible() : true; if (visible != newVisible) { visible = newVisible; return true; } return false; } public void setBounds(Insets insets, int parentWidth, boolean ltr) { int x = horizontalSpring.getOrigin(); int w = horizontalSpring.getSize(); int y = verticalSpring.getOrigin(); int h = verticalSpring.getSize(); if (!ltr) { x = parentWidth - x - w; } component.setBounds(x + insets.left, y + insets.top, w, h); } public void setComponent(Component component) { this.component = component; if (horizontalSpring != null) { horizontalSpring.setComponent(component); } if (verticalSpring != null) { verticalSpring.setComponent(component); } } public Component getComponent() { return component; } /** * Returns true if this component has its size linked to * other components. */ public boolean isLinked(int axis) { if (axis == HORIZONTAL) { return horizontalMaster != null; } assert (axis == VERTICAL); return (verticalMaster != null); } private void setLinkInfo(int axis, LinkInfo linkInfo) { if (axis == HORIZONTAL) { horizontalMaster = linkInfo; } else { assert (axis == VERTICAL); verticalMaster = linkInfo; } } public LinkInfo getLinkInfo(int axis) { return getLinkInfo(axis, true); } private LinkInfo getLinkInfo(int axis, boolean create) { if (axis == HORIZONTAL) { if (horizontalMaster == null && create) { // horizontalMaster field is directly set by adding // us to the LinkInfo. new LinkInfo(HORIZONTAL).add(this); } return horizontalMaster; } else { assert (axis == VERTICAL); if (verticalMaster == null && create) { // verticalMaster field is directly set by adding // us to the LinkInfo. new LinkInfo(VERTICAL).add(this); } return verticalMaster; } } public void clearCachedSize() { if (horizontalMaster != null) { horizontalMaster.clearCachedSize(); } if (verticalMaster != null) { verticalMaster.clearCachedSize(); } } int getLinkSize(int axis, int type) { if (axis == HORIZONTAL) { return horizontalMaster.getSize(axis); } else { assert (axis == VERTICAL); return verticalMaster.getSize(axis); } } } }
  • Other Java examples (source code examples)

    Here is a short list of links related to this Java GroupLayout.java source code file:

    ... this post is sponsored by my books ...

    #1 New Release!

    FP Best Seller

     

    new blog posts

     

    Copyright 1998-2021 Alvin Alexander, alvinalexander.com
    All Rights Reserved.

    A percentage of advertising revenue from
    pages under the /java/jwarehouse URI on this website is
    paid back to open source projects.