History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/GroupLayout.java (1 of 18352 files). Show all

Unified · Split

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