KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > GroupLayout


1 /*
2  * @(#)GroupLayout.java 1.2 06/04/07
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing;
8
9 import java.awt.Component JavaDoc;
10 import java.awt.Container JavaDoc;
11 import java.awt.Dimension JavaDoc;
12 import java.awt.Insets JavaDoc;
13 import java.awt.LayoutManager2 JavaDoc;
14 import java.util.*;
15 import static java.awt.Component.BaselineResizeBehavior JavaDoc;
16 import static javax.swing.LayoutStyle.ComponentPlacement JavaDoc;
17 import static javax.swing.SwingConstants.HORIZONTAL JavaDoc;
18 import static javax.swing.SwingConstants.VERTICAL JavaDoc;
19
20 /**
21  * {@code GroupLayout} is a {@code LayoutManager} that hierarchically
22  * groups components in order to position them in a {@code Container}.
23  * {@code GroupLayout} is intended for use by builders, but may be
24  * hand-coded as well.
25  * Grouping is done by instances of the {@link Group Group} class. {@code
26  * GroupLayout} supports two types of groups. A sequential group
27  * positions its child elements sequentially, one after another. A
28  * parallel group aligns its child elements in one of four ways.
29  * <p>
30  * Each group may contain any number of elements, where an element is
31  * a {@code Group}, {@code Component}, or gap. A gap can be thought
32  * of as an invisible component with a minimum, preferred and maximum
33  * size. In addition {@code GroupLayout} supports a preferred gap,
34  * whose value comes from {@code LayoutStyle}.
35  * <p>
36  * Elements are similar to a spring. Each element has a range as
37  * specified by a minimum, preferred and maximum. Gaps have either a
38  * developer-specified range, or a range determined by {@code
39  * LayoutStyle}. The range for {@code Component}s is determined from
40  * the {@code Component}'s {@code getMinimumSize}, {@code
41  * getPreferredSize} and {@code getMaximumSize} methods. In addition,
42  * when adding {@code Component}s you may specify a particular range
43  * to use instead of that from the component. The range for a {@code
44  * Group} is determined by the type of group. A {@code ParallelGroup}'s
45  * range is the maximum of the ranges of its elements. A {@code
46  * SequentialGroup}'s range is the sum of the ranges of its elements.
47  * <p>
48  * {@code GroupLayout} treats each axis independently. That is, there
49  * is a group representing the horizontal axis, and a group
50  * representing the vertical axis. The horizontal group is
51  * responsible for determining the minimum, preferred and maximum size
52  * along the horizontal axis as well as setting the x and width of the
53  * components contained in it. The vertical group is responsible for
54  * determining the minimum, preferred and maximum size along the
55  * vertical axis as well as setting the y and height of the
56  * components contained in it. Each {@code Component} must exist in both
57  * a horizontal and vertical group, otherwise an {@code IllegalStateException}
58  * is thrown during layout, or when the minimum, preferred or
59  * maximum size is requested.
60  * <p>
61  * The following diagram shows a sequential group along the horizontal
62  * axis. The sequential group contains three components. A parallel group
63  * was used along the vertical axis.
64  * <p align="center">
65  * <img SRC="doc-files/groupLayout.1.gif">
66  * <p>
67  * To reinforce that each axis is treated independently the diagram shows
68  * the range of each group and element along each axis. The
69  * range of each component has been projected onto the axes,
70  * and the groups are rendered in blue (horizontal) and red (vertical).
71  * For readability there is a gap between each of the elements in the
72  * sequential group.
73  * <p>
74  * The sequential group along the horizontal axis is rendered as a solid
75  * blue line. Notice the sequential group is the sum of the children elements
76  * it contains.
77  * <p>
78  * Along the vertical axis the parallel group is the maximum of the height
79  * of each of the components. As all three components have the same height,
80  * the parallel group has the same height.
81  * <p>
82  * The following diagram shows the same three components, but with the
83  * parallel group along the horizontal axis and the sequential group along
84  * the vertical axis.
85  * <p>
86  * <p align="center">
87  * <img SRC="doc-files/groupLayout.2.gif">
88  * <p>
89  * As {@code c1} is the largest of the three components, the parallel
90  * group is sized to {@code c1}. As {@code c2} and {@code c3} are smaller
91  * than {@code c1} they are aligned based on the alignment specified
92  * for the component (if specified) or the default alignment of the
93  * parallel group. In the diagram {@code c2} and {@code c3} were created
94  * with an alignment of {@code LEADING}. If the component orientation were
95  * right-to-left then {@code c2} and {@code c3} would be positioned on
96  * the opposite side.
97  * <p>
98  * The following diagram shows a sequential group along both the horizontal
99  * and vertical axis.
100  * <p align="center">
101  * <img SRC="doc-files/groupLayout.3.gif">
102  * <p>
103  * {@code GroupLayout} provides the ability to insert gaps between
104  * {@code Component}s. The size of the gap is determined by an
105  * instance of {@code LayoutStyle}. This may be turned on using the
106  * {@code setAutoCreateGaps} method. Similarly, you may use
107  * the {@code setAutoCreateContainerGaps} method to insert gaps
108  * between components that touch the edge of the parent container and the
109  * container.
110  * <p>
111  * The following builds a panel consisting of two labels in
112  * one column, followed by two textfields in the next column:
113  * <pre>
114  * JComponent panel = ...;
115  * GroupLayout layout = new GroupLayout(panel);
116  * panel.setLayout(layout);
117  *
118  * // Turn on automatically adding gaps between components
119  * layout.setAutoCreateGaps(true);
120  *
121  * // Turn on automatically creating gaps between components that touch
122  * // the edge of the container and the container.
123  * layout.setAutoCreateContainerGaps(true);
124  *
125  * // Create a sequential group for the horizontal axis.
126  *
127  * GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
128  *
129  * // The sequential group in turn contains two parallel groups.
130  * // One parallel group contains the labels, the other the text fields.
131  * // Putting the labels in a parallel group along the horizontal axis
132  * // positions them at the same x location.
133  * //
134  * // Variable indentation is used to reinforce the level of grouping.
135  * hGroup.addGroup(layout.createParallelGroup().
136  * addComponent(label1).addComponent(label2));
137  * hGroup.addGroup(layout.createParallelGroup().
138  * addComponent(tf1).addComponent(tf2));
139  * layout.setHorizontalGroup(hGroup);
140  *
141  * // Create a sequential group for the vertical axis.
142  * GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
143  *
144  * // The sequential group contains two parallel groups that align
145  * // the contents along the baseline. The first parallel group contains
146  * // the first label and text field, and the second parallel group contains
147  * // the second label and text field. By using a sequential group
148  * // the labels and text fields are positioned vertically after one another.
149  * vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
150  * addComponent(label1).addComponent(tf1));
151  * vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
152  * addComponent(label2).addComponent(tf2));
153  * layout.setVerticalGroup(vGroup);
154  * </pre>
155  * <p>
156  * When run the following is produced.
157  * <p align="center">
158  * <img SRC="doc-files/groupLayout.example.png">
159  * <p>
160  * This layout consists of the following.
161  * <ul><li>The horizontal axis consists of a sequential group containing two
162  * parallel groups. The first parallel group contains the labels,
163  * and the second parallel group contains the text fields.
164  * <li>The vertical axis consists of a sequential group
165  * containing two parallel groups. The parallel groups are configured
166  * to align their components along the baseline. The first parallel
167  * group contains the first label and first text field, and
168  * the second group consists of the second label and second
169  * text field.
170  * </ul>
171  * There are a couple of things to notice in this code:
172  * <ul>
173  * <li>You need not explicitly add the components to the container; this
174  * is indirectly done by using one of the {@code add} methods of
175  * {@code Group}.
176  * <li>The various {@code add} methods return
177  * the caller. This allows for easy chaining of invocations. For
178  * example, {@code group.addComponent(label1).addComponent(label2);} is
179  * equivalent to
180  * {@code group.addComponent(label1); group.addComponent(label2);}.
181  * <li>There are no public constructors for {@code Group}s; instead
182  * use the create methods of {@code GroupLayout}.
183  * </ul>
184  *
185  * @author Tomas Pavek
186  * @author Jan Stola
187  * @author Scott Violet
188  * @version 1.2, 04/07/06
189  * @since 1.6
190  */

191 public class GroupLayout implements LayoutManager2 JavaDoc {
192     // Used in size calculations
193
private static final int MIN_SIZE = 0;
194     
195     private static final int PREF_SIZE = 1;
196     
197     private static final int MAX_SIZE = 2;
198     
199     // Used by prepare, indicates min, pref or max isn't going to be used.
200
private static final int SPECIFIC_SIZE = 3;
201     
202     private static final int UNSET = Integer.MIN_VALUE;
203     
204     /**
205      * Indicates the size from the component or gap should be used for a
206      * particular range value.
207      *
208      * @see Group
209      */

210     public static final int DEFAULT_SIZE = -1;
211     
212     /**
213      * Indicates the preferred size from the component or gap should
214      * be used for a particular range value.
215      *
216      * @see Group
217      */

218     public static final int PREFERRED_SIZE = -2;
219     
220     // Whether or not we automatically try and create the preferred
221
// padding between components.
222
private boolean autocreatePadding;
223     
224     // Whether or not we automatically try and create the preferred
225
// padding between components the touch the edge of the container and
226
// the container.
227
private boolean autocreateContainerPadding;
228     
229     /**
230      * Group responsible for layout along the horizontal axis. This is NOT
231      * the user specified group, use getHorizontalGroup to dig that out.
232      */

233     private Group horizontalGroup;
234     
235     /**
236      * Group responsible for layout along the vertical axis. This is NOT
237      * the user specified group, use getVerticalGroup to dig that out.
238      */

239     private Group verticalGroup;
240     
241     // Maps from Component to ComponentInfo. This is used for tracking
242
// information specific to a Component.
243
private Map<Component JavaDoc,ComponentInfo> componentInfos;
244     
245     // Container we're doing layout for.
246
private Container JavaDoc host;
247     
248     // Used by areParallelSiblings, cached to avoid excessive garbage.
249
private Set<Spring JavaDoc> tmpParallelSet;
250     
251     // Indicates Springs have changed in some way since last change.
252
private boolean springsChanged;
253     
254     // Indicates invalidateLayout has been invoked.
255
private boolean isValid;
256     
257     // Whether or not any preferred padding (or container padding) springs
258
// exist
259
private boolean hasPreferredPaddingSprings;
260     
261     /**
262      * The LayoutStyle instance to use, if null the sharedInstance is used.
263      */

264     private LayoutStyle JavaDoc layoutStyle;
265     
266     /**
267      * If true, components that are not visible are treated as though they
268      * aren't there.
269      */

270     private boolean honorsVisibility;
271     
272     
273     /**
274      * Enumeration of the possible ways {@code ParallelGroup} can align
275      * its children.
276      *
277      * @see #createParallelGroup(Alignment)
278      * @since 1.6
279      */

280     public enum Alignment {
281         /**
282          * Indicates the elements should be
283          * aligned to the origin. For the horizontal axis with a left to
284          * right orientation this means aligned to the left edge. For the
285          * vertical axis leading means aligned to the top edge.
286          *
287          * @see #createParallelGroup(Alignment)
288          */

289         LEADING,
290         
291         /**
292          * Indicates the elements should be aligned to the end of the
293          * region. For the horizontal axis with a left to right
294          * orientation this means aligned to the right edge. For the
295          * vertical axis trailing means aligned to the bottom edge.
296          *
297          * @see #createParallelGroup(Alignment)
298          */

299         TRAILING,
300         
301         /**
302          * Indicates the elements should be centered in
303          * the region.
304          *
305          * @see #createParallelGroup(Alignment)
306          */

307         CENTER,
308         
309         /**
310          * Indicates the elements should be aligned along
311          * their baseline.
312          *
313          * @see #createParallelGroup(Alignment)
314          * @see #createBaselineGroup(boolean,boolean)
315          */

316         BASELINE
317     }
318     
319     
320     private static void checkSize(int min, int pref, int max,
321             boolean isComponentSpring) {
322         checkResizeType(min, isComponentSpring);
323         if (!isComponentSpring && pref < 0) {
324             throw new IllegalArgumentException JavaDoc("Pref must be >= 0");
325         } else if (isComponentSpring) {
326             checkResizeType(pref, true);
327         }
328         checkResizeType(max, isComponentSpring);
329         checkLessThan(min, pref);
330         checkLessThan(pref, max);
331     }
332     
333     private static void checkResizeType(int type, boolean isComponentSpring) {
334         if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE &&
335                 type != PREFERRED_SIZE) ||
336                 (!isComponentSpring && type != PREFERRED_SIZE))) {
337             throw new IllegalArgumentException JavaDoc("Invalid size");
338         }
339     }
340     
341     private static void checkLessThan(int min, int max) {
342         if (min >= 0 && max >= 0 && min > max) {
343             throw new IllegalArgumentException JavaDoc(
344                     "Following is not met: min<=pref<=max");
345         }
346     }
347     
348     /**
349      * Creates a {@code GroupLayout} for the specified {@code Container}.
350      *
351      * @param host the {@code Container} the {@code GroupLayout} is
352      * the {@code LayoutManager} for
353      * @throws IllegalArgumentException if host is {@code null}
354      */

355     public GroupLayout(Container JavaDoc host) {
356         if (host == null) {
357             throw new IllegalArgumentException JavaDoc("Container must be non-null");
358         }
359         honorsVisibility = true;
360         this.host = host;
361         setHorizontalGroup(createParallelGroup(Alignment.LEADING, true));
362         setVerticalGroup(createParallelGroup(Alignment.LEADING, true));
363         componentInfos = new HashMap<Component JavaDoc,ComponentInfo>();
364         tmpParallelSet = new HashSet<Spring JavaDoc>();
365     }
366     
367     /**
368      * Sets whether component visiblity is considered when sizing and
369      * positioning components. A value of {@code true} indicates that
370      * non-visible components should not be treated as part of the
371      * layout. A value of {@code false} indicates that components should be
372      * positioned and sized regardless of visibility.
373      * <p>
374      * A value of {@code false} is useful when the visibility of components
375      * is dynamically adjusted and you don't want surrounding components and
376      * the sizing to change.
377      * <p>
378      * The specified value is used for components that do not have an
379      * explicit visibility specified.
380      * <p>
381      * The default is {@code true}.
382      *
383      * @param honorsVisibility whether component visiblity is considered when
384      * sizing and positioning components
385      * @see #setHonorsVisibility(Component,Boolean)
386      */

387     public void setHonorsVisibility(boolean honorsVisibility) {
388         if (this.honorsVisibility != honorsVisibility) {
389             this.honorsVisibility = honorsVisibility;
390             springsChanged = true;
391             isValid = false;
392             invalidateHost();
393         }
394     }
395     
396     /**
397      * Returns whether component visiblity is considered when sizing and
398      * positioning components.
399      *
400      * @return whether component visiblity is considered when sizing and
401      * positioning components
402      */

403     public boolean getHonorsVisibility() {
404         return honorsVisibility;
405     }
406     
407     /**
408      * Sets whether the component's visiblity is considered for
409      * sizing and positioning. A value of {@code Boolean.TRUE}
410      * indicates that if {@code component} is not visible it should
411      * not be treated as part of the layout. A value of {@code false}
412      * indicates that {@code component} is positioned and sized
413      * regardless of it's visibility. A value of {@code null}
414      * indicates the value specified by the single argument method {@code
415      * setHonorsVisibility} should be used.
416      * <p>
417      * If {@code component} is not a child of the {@code Container} this
418      * {@code GroupLayout} is managine, it will be added to the
419      * {@code Container}.
420      *
421      * @param component the component
422      * @param honorsVisibility whether {@code component}'s visiblity should be
423      * considered for sizing and positioning
424      * @throws IllegalArgumentException if {@code component} is {@code null}
425      * @see #setHonorsVisibility(Component,Boolean)
426      */

427     public void setHonorsVisibility(Component JavaDoc component,
428             Boolean JavaDoc honorsVisibility) {
429         if (component == null) {
430             throw new IllegalArgumentException JavaDoc("Component must be non-null");
431         }
432         getComponentInfo(component).setHonorsVisibility(honorsVisibility);
433         springsChanged = true;
434         isValid = false;
435         invalidateHost();
436     }
437     
438     /**
439      * Sets whether a gap between components should automatically be
440      * created. For example, if this is {@code true} and you add two
441      * components to a {@code SequentialGroup} a gap between the
442      * two components is automatically be created. The default is
443      * {@code false}.
444      *
445      * @param autoCreatePadding whether a gap between components is
446      * automatically created
447      */

448     public void setAutoCreateGaps(boolean autoCreatePadding) {
449         if (this.autocreatePadding != autoCreatePadding) {
450             this.autocreatePadding = autoCreatePadding;
451             invalidateHost();
452         }
453     }
454     
455     /**
456      * Returns {@code true} if gaps between components are automatically
457      * created.
458      *
459      * @return {@code true} if gaps between components are automatically
460      * created
461      */

462     public boolean getAutoCreateGaps() {
463         return autocreatePadding;
464     }
465     
466     /**
467      * Sets whether a gap between the container and components that
468      * touch the border of the container should automatically be
469      * created. The default is {@code false}.
470      *
471      * @param autoCreateContainerPadding whether a gap between the container and
472      * components that touch the border of the container should
473      * automatically be created
474      */

475     public void setAutoCreateContainerGaps(boolean autoCreateContainerPadding){
476         if (this.autocreateContainerPadding != autoCreateContainerPadding) {
477             this.autocreateContainerPadding = autoCreateContainerPadding;
478             horizontalGroup = createTopLevelGroup(getHorizontalGroup());
479             verticalGroup = createTopLevelGroup(getVerticalGroup());
480             invalidateHost();
481         }
482     }
483     
484     /**
485      * Returns {@code true} if gaps between the container and components that
486      * border the container are automatically created.
487      *
488      * @return {@code true} if gaps between the container and components that
489      * border the container are automatically created
490      */

491     public boolean getAutoCreateContainerGaps() {
492         return autocreateContainerPadding;
493     }
494     
495     /**
496      * Sets the {@code Group} that positions and sizes
497      * components along the horizontal axis.
498      *
499      * @param group the {@code Group} that positions and sizes
500      * components along the horizontal axis
501      * @throws IllegalArgumentException if group is {@code null}
502      */

503     public void setHorizontalGroup(Group group) {
504         if (group == null) {
505             throw new IllegalArgumentException JavaDoc("Group must be non-null");
506         }
507         horizontalGroup = createTopLevelGroup(group);
508         invalidateHost();
509     }
510     
511     /**
512      * Returns the {@code Group} that positions and sizes components
513      * along the horizontal axis.
514      *
515      * @return the {@code Group} responsible for positioning and
516      * sizing component along the horizontal axis
517      */

518     private Group getHorizontalGroup() {
519         int index = 0;
520         if (horizontalGroup.springs.size() > 1) {
521             index = 1;
522         }
523         return (Group)horizontalGroup.springs.get(index);
524     }
525     
526     /**
527      * Sets the {@code Group} that positions and sizes
528      * components along the vertical axis.
529      *
530      * @param group the {@code Group} that positions and sizes
531      * components along the vertical axis
532      * @throws IllegalArgumentException if group is {@code null}
533      */

534     public void setVerticalGroup(Group group) {
535         if (group == null) {
536             throw new IllegalArgumentException JavaDoc("Group must be non-null");
537         }
538         verticalGroup = createTopLevelGroup(group);
539         invalidateHost();
540     }
541     
542     /**
543      * Returns the {@code Group} that positions and sizes components
544      * along the vertical axis.
545      *
546      * @return the {@code Group} responsible for positioning and
547      * sizing component along the vertical axis
548      */

549     private Group getVerticalGroup() {
550         int index = 0;
551         if (verticalGroup.springs.size() > 1) {
552             index = 1;
553         }
554         return (Group)verticalGroup.springs.get(index);
555     }
556     
557     /**
558      * Wraps the user specified group in a sequential group. If
559      * container gaps should be generated the necessary springs are
560      * added.
561      */

562     private Group createTopLevelGroup(Group specifiedGroup) {
563         SequentialGroup group = createSequentialGroup();
564         if (getAutoCreateContainerGaps()) {
565             group.addSpring(new ContainerAutoPreferredGapSpring());
566             group.addGroup(specifiedGroup);
567             group.addSpring(new ContainerAutoPreferredGapSpring());
568         } else {
569             group.addGroup(specifiedGroup);
570         }
571         return group;
572     }
573     
574     /**
575      * Creates and returns a {@code SequentialGroup}.
576      *
577      * @return a new {@code SequentialGroup}
578      */

579     public SequentialGroup createSequentialGroup() {
580         return new SequentialGroup();
581     }
582     
583     /**
584      * Creates and returns a {@code ParallelGroup} with an alignment of
585      * {@code Alignment.LEADING}. This is a cover method for the more
586      * general {@code createParallelGroup(Alignment)} method.
587      *
588      * @return a new {@code ParallelGroup}
589      * @see #createParallelGroup(Alignment)
590      */

591     public ParallelGroup createParallelGroup() {
592         return createParallelGroup(Alignment.LEADING);
593     }
594     
595     /**
596      * Creates and returns a {@code ParallelGroup} with the specified
597      * alignment. This is a cover method for the more general {@code
598      * createParallelGroup(Alignment,boolean)} method with {@code true}
599      * supplied for the second argument.
600      *
601      * @param alignment the alignment for the elements of the group
602      * @throws IllegalArgumentException if {@code alignment} is {@code null}
603      * @return a new {@code ParallelGroup}
604      * @see #createBaselineGroup
605      * @see ParallelGroup
606      */

607     public ParallelGroup createParallelGroup(Alignment alignment) {
608         return createParallelGroup(alignment, true);
609     }
610     
611     /**
612      * Creates and returns a {@code ParallelGroup} with the specified
613      * alignment and resize behavior. The {@code
614      * alignment} argument specifies how children elements are
615      * positioned that do not fill the group. For example, if a {@code
616      * ParallelGroup} with an alignment of {@code TRAILING} is given
617      * 100 and a child only needs 50, the child is
618      * positioned at the position 50 (with a component orientation of
619      * left-to-right).
620      * <p>
621      * Baseline alignment is only useful when used along the vertical
622      * axis. A {@code ParallelGroup} created with a baseline alignment
623      * along the horizontal axis is treated as {@code LEADING}.
624      * <p>
625      * Refer to {@link GroupLayout.ParallelGroup ParallelGroup} for details on
626      * the behavior of baseline groups.
627      *
628      * @param alignment the alignment for the elements of the group
629      * @param resizable {@code true} if the group is resizable; if the group
630      * is not resizable the preferred size is used for the
631      * minimum and maximum size of the group
632      * @throws IllegalArgumentException if {@code alignment} is {@code null}
633      * @return a new {@code ParallelGroup}
634      * @see #createBaselineGroup
635      * @see GroupLayout.ParallelGroup
636      */

637     public ParallelGroup createParallelGroup(Alignment alignment,
638             boolean resizable){
639         if (alignment == Alignment.BASELINE) {
640             return new BaselineGroup(resizable);
641         }
642         return new ParallelGroup(alignment, resizable);
643     }
644
645     /**
646      * Creates and returns a {@code ParallelGroup} that aligns it's
647      * elements along the baseline.
648      *
649      * @param resizable whether the group is resizable
650      * @param anchorBaselineToTop whether the baseline is anchored to
651      * the top or bottom of the group
652      * @see #createBaselineGroup
653      * @see ParallelGroup
654      */

655     public ParallelGroup createBaselineGroup(boolean resizable,
656             boolean anchorBaselineToTop) {
657         return new BaselineGroup(resizable, anchorBaselineToTop);
658     }
659     
660     /**
661      * Forces the specified components to have the same size
662      * regardless of their preferred, minimum or maximum sizes. Components that
663      * are linked are given the maximum of the preferred size of each of
664      * the linked components. For example, if you link two components with
665      * a preferred width of 10 and 20, both components are given a width of 20.
666      * <p>
667      * This can be used multiple times to force any number of
668      * components to share the same size.
669      * <p>
670      * Linked Components are not be resizable.
671      *
672      * @param components the {@code Component}s that are to have the same size
673      * @throws IllegalArgumentException if {@code components} is
674      * {@code null}, or contains {@code null}
675      * @see #linkSize(int,Component[])
676      */

677     public void linkSize(Component JavaDoc... components) {
678         linkSize(SwingConstants.HORIZONTAL, components);
679         linkSize(SwingConstants.VERTICAL, components);
680     }
681     
682     /**
683      * Forces the specified components to have the same size along the
684      * specified axis regardless of their preferred, minimum or
685      * maximum sizes. Components that are linked are given the maximum
686      * of the preferred size of each of the linked components. For
687      * example, if you link two components along the horizontal axis
688      * and the preferred width is 10 and 20, both components are given
689      * a width of 20.
690      * <p>
691      * This can be used multiple times to force any number of
692      * components to share the same size.
693      * <p>
694      * Linked {@code Component}s are not be resizable.
695      *
696      * @param components the {@code Component}s that are to have the same size
697      * @param axis the axis to link the size along; one of
698      * {@code SwingConstants.HORIZONTAL} or
699      * {@code SwingConstans.VERTICAL}
700      * @throws IllegalArgumentException if {@code components} is
701      * {@code null}, or contains {@code null}; or {@code axis}
702      * is not {@code SwingConstants.HORIZONTAL} or
703      * {@code SwingConstants.VERTICAL}
704      */

705     public void linkSize(int axis, Component JavaDoc... components) {
706         if (components == null) {
707             throw new IllegalArgumentException JavaDoc("Components must be non-null");
708         }
709         for (int counter = components.length - 1; counter >= 0; counter--) {
710             Component JavaDoc c = components[counter];
711             if (components[counter] == null) {
712                 throw new IllegalArgumentException JavaDoc(
713                         "Components must be non-null");
714             }
715             // Force the component to be added
716
getComponentInfo(c);
717         }
718         int glAxis;
719         if (axis == SwingConstants.HORIZONTAL) {
720             glAxis = HORIZONTAL;
721         } else if (axis == SwingConstants.VERTICAL) {
722             glAxis = VERTICAL;
723         } else {
724             throw new IllegalArgumentException JavaDoc("Axis must be one of " +
725                     "SwingConstants.HORIZONTAL or SwingConstants.VERTICAL");
726         }
727         LinkInfo master = getComponentInfo(
728                 components[components.length - 1]).getLinkInfo(glAxis);
729         for (int counter = components.length - 2; counter >= 0; counter--) {
730             master.add(getComponentInfo(components[counter]));
731         }
732         invalidateHost();
733     }
734     
735     /**
736      * Replaces an existing component with a new one.
737      *
738      * @param existingComponent the component that should be removed
739      * and replaced with {@code newComponent}
740      * @param newComponent the component to put in
741      * {@code existingComponent}'s place
742      * @throws IllegalArgumentException if either of the components are
743      * {@code null} or {@code existingComponent} is not being managed
744      * by this layout manager
745      */

746     public void replace(Component JavaDoc existingComponent, Component JavaDoc newComponent) {
747         if (existingComponent == null || newComponent == null) {
748             throw new IllegalArgumentException JavaDoc("Components must be non-null");
749         }
750         // Make sure all the components have been registered, otherwise we may
751
// not update the correct Springs.
752
if (springsChanged) {
753             registerComponents(horizontalGroup, HORIZONTAL);
754             registerComponents(verticalGroup, VERTICAL);
755         }
756         ComponentInfo info = componentInfos.remove(existingComponent);
757         if (info == null) {
758             throw new IllegalArgumentException JavaDoc("Component must already exist");
759         }
760         host.remove(existingComponent);
761         if (newComponent.getParent() != host) {
762             host.add(newComponent);
763         }
764         info.setComponent(newComponent);
765         componentInfos.put(newComponent, info);
766         invalidateHost();
767     }
768     
769     /**
770      * Sets the {@code LayoutStyle} used to calculate the preferred
771      * gaps between components. A value of {@code null} indicates the
772      * shared instance of {@code LayoutStyle} should be used.
773      *
774      * @param layoutStyle the {@code LayoutStyle} to use
775      * @see LayoutStyle
776      */

777     public void setLayoutStyle(LayoutStyle JavaDoc layoutStyle) {
778         this.layoutStyle = layoutStyle;
779         invalidateHost();
780     }
781     
782     /**
783      * Returns the {@code LayoutStyle} used for calculating the preferred
784      * gap between components. This returns the value specified to
785      * {@code setLayoutStyle}, which may be {@code null}.
786      *
787      * @return the {@code LayoutStyle} used for calculating the preferred
788      * gap between components
789      */

790     public LayoutStyle JavaDoc getLayoutStyle() {
791         return layoutStyle;
792     }
793     
794     private LayoutStyle JavaDoc getLayoutStyle0() {
795         LayoutStyle JavaDoc layoutStyle = getLayoutStyle();
796         if (layoutStyle == null) {
797             layoutStyle = LayoutStyle.getInstance();
798         }
799         return layoutStyle;
800     }
801     
802     private void invalidateHost() {
803         if (host instanceof JComponent JavaDoc) {
804             ((JComponent JavaDoc)host).revalidate();
805         } else {
806             host.invalidate();
807         }
808         host.repaint();
809     }
810     
811     //
812
// LayoutManager
813
//
814
/**
815      * Notification that a {@code Component} has been added to
816      * the parent container. You should not invoke this method
817      * directly, instead you should use one of the {@code Group}
818      * methods to add a {@code Component}.
819      *
820      * @param name the string to be associated with the component
821      * @param component the {@code Component} to be added
822      */

823     public void addLayoutComponent(String JavaDoc name, Component JavaDoc component) {
824     }
825     
826     /**
827      * Notification that a {@code Component} has been removed from
828      * the parent container. You should not invoke this method
829      * directly, instead invoke {@code remove} on the parent
830      * {@code Container}.
831      *
832      * @param component the component to be removed
833      * @see java.awt.Component#remove
834      */

835     public void removeLayoutComponent(Component JavaDoc component) {
836         ComponentInfo info = componentInfos.remove(component);
837         if (info != null) {
838             info.dispose();
839             springsChanged = true;
840             isValid = false;
841         }
842     }
843     
844     /**
845      * Returns the preferred size for the specified container.
846      *
847      * @param parent the container to return the preferred size for
848      * @return the preferred size for {@code parent}
849      * @throws IllegalArgumentException if {@code parent} is not
850      * the same {@code Container} this was created with
851      * @throws IllegalStateException if any of the components added to
852      * this layout are not in both a horizontal and vertical group
853      * @see java.awt.Container#getPreferredSize
854      */

855     public Dimension JavaDoc preferredLayoutSize(Container JavaDoc parent) {
856         checkParent(parent);
857         prepare(PREF_SIZE);
858         return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),
859                 verticalGroup.getPreferredSize(VERTICAL));
860     }
861     
862     /**
863      * Returns the minimum size for the specified container.
864      *
865      * @param parent the container to return the size for
866      * @return the minimum size for {@code parent}
867      * @throws IllegalArgumentException if {@code parent} is not
868      * the same {@code Container} that this was created with
869      * @throws IllegalStateException if any of the components added to
870      * this layout are not in both a horizontal and vertical group
871      * @see java.awt.Container#getMinimumSize
872      */

873     public Dimension JavaDoc minimumLayoutSize(Container JavaDoc parent) {
874         checkParent(parent);
875         prepare(MIN_SIZE);
876         return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),
877                 verticalGroup.getMinimumSize(VERTICAL));
878     }
879     
880     /**
881      * Lays out the specified container.
882      *
883      * @param parent the container to be laid out
884      * @throws IllegalStateException if any of the components added to
885      * this layout are not in both a horizontal and vertical group
886      */

887     public void layoutContainer(Container JavaDoc parent) {
888         // Step 1: Prepare for layout.
889
prepare(SPECIFIC_SIZE);
890         Insets JavaDoc insets = parent.getInsets();
891         int width = parent.getWidth() - insets.left - insets.right;
892         int height = parent.getHeight() - insets.top - insets.bottom;
893         boolean ltr = isLeftToRight();
894         if (getAutoCreateGaps() || getAutoCreateContainerGaps() ||
895                 hasPreferredPaddingSprings) {
896             // Step 2: Calculate autopadding springs
897
calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0,
898                     width);
899             calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0,
900                     height);
901         }
902         // Step 3: set the size of the groups.
903
horizontalGroup.setSize(HORIZONTAL, 0, width);
904         verticalGroup.setSize(VERTICAL, 0, height);
905         // Step 4: apply the size to the components.
906
for (ComponentInfo info : componentInfos.values()) {
907             info.setBounds(insets, width, ltr);
908         }
909     }
910     
911     //
912
// LayoutManager2
913
//
914
/**
915      * Notification that a {@code Component} has been added to
916      * the parent container. You should not invoke this method
917      * directly, instead you should use one of the {@code Group}
918      * methods to add a {@code Component}.
919      *
920      * @param component the component added
921      * @param constraints description of where to place the component
922      */

923     public void addLayoutComponent(Component JavaDoc component, Object JavaDoc constraints) {
924     }
925     
926     /**
927      * Returns the maximum size for the specified container.
928      *
929      * @param parent the container to return the size for
930      * @return the maximum size for {@code parent}
931      * @throws IllegalArgumentException if {@code parent} is not
932      * the same {@code Container} that this was created with
933      * @throws IllegalStateException if any of the components added to
934      * this layout are not in both a horizontal and vertical group
935      * @see java.awt.Container#getMaximumSize
936      */

937     public Dimension JavaDoc maximumLayoutSize(Container JavaDoc parent) {
938         checkParent(parent);
939         prepare(MAX_SIZE);
940         return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),
941                 verticalGroup.getMaximumSize(VERTICAL));
942     }
943     
944     /**
945      * Returns the alignment along the x axis. This specifies how
946      * the component would like to be aligned relative to other
947      * components. The value should be a number between 0 and 1
948      * where 0 represents alignment along the origin, 1 is aligned
949      * the furthest away from the origin, 0.5 is centered, etc.
950      *
951      * @param parent the {@code Container} hosting this {@code LayoutManager}
952      * @throws IllegalArgumentException if {@code parent} is not
953      * the same {@code Container} that this was created with
954      * @return the alignment; this implementation returns {@code .5}
955      */

956     public float getLayoutAlignmentX(Container JavaDoc parent) {
957         checkParent(parent);
958         return .5f;
959     }
960     
961     /**
962      * Returns the alignment along the y axis. This specifies how
963      * the component would like to be aligned relative to other
964      * components. The value should be a number between 0 and 1
965      * where 0 represents alignment along the origin, 1 is aligned
966      * the furthest away from the origin, 0.5 is centered, etc.
967      *
968      * @param parent the {@code Container} hosting this {@code LayoutManager}
969      * @throws IllegalArgumentException if {@code parent} is not
970      * the same {@code Container} that this was created with
971      * @return alignment; this implementation returns {@code .5}
972      */

973     public float getLayoutAlignmentY(Container JavaDoc parent) {
974         checkParent(parent);
975         return .5f;
976     }
977     
978     /**
979      * Invalidates the layout, indicating that if the layout manager
980      * has cached information it should be discarded.
981      *
982      * @param parent the {@code Container} hosting this LayoutManager
983      * @throws IllegalArgumentException if {@code parent} is not
984      * the same {@code Container} that this was created with
985      */

986     public void invalidateLayout(Container JavaDoc parent) {
987         checkParent(parent);
988         // invalidateLayout is called from Container.invalidate, which
989
// does NOT grab the treelock. All other methods do. To make sure
990
// there aren't any possible threading problems we grab the tree lock
991
// here.
992
synchronized(parent.getTreeLock()) {
993             isValid = false;
994         }
995     }
996     
997     private void prepare(int sizeType) {
998         boolean visChanged = false;
999         // Step 1: If not-valid, clear springs and update visibility.
1000
if (!isValid) {
1001            isValid = true;
1002            horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);
1003            verticalGroup.setSize(VERTICAL, UNSET, UNSET);
1004            for (ComponentInfo ci : componentInfos.values()) {
1005                if (ci.updateVisibility()) {
1006                    visChanged = true;
1007                }
1008                ci.clearCachedSize();
1009            }
1010        }
1011        // Step 2: Make sure components are bound to ComponentInfos
1012
if (springsChanged) {
1013            registerComponents(horizontalGroup, HORIZONTAL);
1014            registerComponents(verticalGroup, VERTICAL);
1015        }
1016        // Step 3: Adjust the autopadding. This removes existing
1017
// autopadding, then recalculates where it should go.
1018
if (springsChanged || visChanged) {
1019            checkComponents();
1020            horizontalGroup.removeAutopadding();
1021            verticalGroup.removeAutopadding();
1022            if (getAutoCreateGaps()) {
1023                insertAutopadding(true);
1024            } else if (hasPreferredPaddingSprings ||
1025                    getAutoCreateContainerGaps()) {
1026                insertAutopadding(false);
1027            }
1028            springsChanged = false;
1029        }
1030        // Step 4: (for min/pref/max size calculations only) calculate the
1031
// autopadding. This invokes for unsetting the calculated values, then
1032
// recalculating them.
1033
// If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this
1034
// step will be done later on.
1035
if (sizeType != SPECIFIC_SIZE && (getAutoCreateGaps() ||
1036                getAutoCreateContainerGaps() || hasPreferredPaddingSprings)) {
1037            calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0);
1038            calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);
1039        }
1040    }
1041    
1042    private void calculateAutopadding(Group group, int axis, int sizeType,
1043            int origin, int size) {
1044        group.unsetAutopadding();
1045        switch(sizeType) {
1046            case MIN_SIZE:
1047                size = group.getMinimumSize(axis);
1048                break;
1049            case PREF_SIZE:
1050                size = group.getPreferredSize(axis);
1051                break;
1052            case MAX_SIZE:
1053                size = group.getMaximumSize(axis);
1054                break;
1055            default:
1056                break;
1057        }
1058        group.setSize(axis, origin, size);
1059        group.calculateAutopadding(axis);
1060    }
1061    
1062    private void checkComponents() {
1063        for (ComponentInfo info : componentInfos.values()) {
1064            if (info.horizontalSpring == null) {
1065                throw new IllegalStateException JavaDoc(info.component +
1066                        " is not attached to a horizontal group");
1067            }
1068            if (info.verticalSpring == null) {
1069                throw new IllegalStateException JavaDoc(info.component +
1070                        " is not attached to a vertical group");
1071            }
1072        }
1073    }
1074    
1075    private void registerComponents(Group group, int axis) {
1076        List<Spring JavaDoc> springs = group.springs;
1077        for (int counter = springs.size() - 1; counter >= 0; counter--) {
1078            Spring JavaDoc spring = springs.get(counter);
1079            if (spring instanceof ComponentSpring) {
1080                ((ComponentSpring)spring).installIfNecessary(axis);
1081            } else if (spring instanceof Group) {
1082                registerComponents((Group)spring, axis);
1083            }
1084        }
1085    }
1086    
1087    private Dimension JavaDoc adjustSize(int width, int height) {
1088        Insets JavaDoc insets = host.getInsets();
1089        return new Dimension JavaDoc(width + insets.left + insets.right,
1090                height + insets.top + insets.bottom);
1091    }
1092    
1093    private void checkParent(Container JavaDoc parent) {
1094        if (parent != host) {
1095            throw new IllegalArgumentException JavaDoc(
1096                    "GroupLayout can only be used with one Container at a time");
1097        }
1098    }
1099    
1100    /**
1101     * Returns the {@code ComponentInfo} for the specified Component,
1102     * creating one if necessary.
1103     */

1104    private ComponentInfo getComponentInfo(Component JavaDoc component) {
1105        ComponentInfo info = (ComponentInfo)componentInfos.get(component);
1106        if (info == null) {
1107            info = new ComponentInfo(component);
1108            componentInfos.put(component, info);
1109            if (component.getParent() != host) {
1110                host.add(component);
1111            }
1112        }
1113        return info;
1114    }
1115    
1116    /**
1117     * Adjusts the autopadding springs for the horizontal and vertical
1118     * groups. If {@code insert} is {@code true} this will insert auto padding
1119     * springs, otherwise this will only adjust the springs that
1120     * comprise auto preferred padding springs.
1121     */

1122    private void insertAutopadding(boolean insert) {
1123        horizontalGroup.insertAutopadding(HORIZONTAL,
1124                new ArrayList<AutoPreferredGapSpring>(1),
1125                new ArrayList<AutoPreferredGapSpring>(1),
1126                new ArrayList<ComponentSpring>(1),
1127                new ArrayList<ComponentSpring>(1), insert);
1128        verticalGroup.insertAutopadding(VERTICAL,
1129                new ArrayList<AutoPreferredGapSpring>(1),
1130                new ArrayList<AutoPreferredGapSpring>(1),
1131                new ArrayList<ComponentSpring>(1),
1132                new ArrayList<ComponentSpring>(1), insert);
1133    }
1134    
1135    /**
1136     * Returns {@code true} if the two Components have a common ParallelGroup
1137     * ancestor along the particular axis.
1138     */

1139    private boolean areParallelSiblings(Component JavaDoc source, Component JavaDoc target,
1140            int axis) {
1141        ComponentInfo sourceInfo = getComponentInfo(source);
1142        ComponentInfo targetInfo = getComponentInfo(target);
1143        Spring JavaDoc sourceSpring;
1144        Spring JavaDoc targetSpring;
1145        if (axis == HORIZONTAL) {
1146            sourceSpring = sourceInfo.horizontalSpring;
1147            targetSpring = targetInfo.horizontalSpring;
1148        } else {
1149            sourceSpring = sourceInfo.verticalSpring;
1150            targetSpring = targetInfo.verticalSpring;
1151        }
1152        Set<Spring JavaDoc> sourcePath = tmpParallelSet;
1153        sourcePath.clear();
1154        Spring JavaDoc spring = sourceSpring.getParent();
1155        while (spring != null) {
1156            sourcePath.add(spring);
1157            spring = spring.getParent();
1158        }
1159        spring = targetSpring.getParent();
1160        while (spring != null) {
1161            if (sourcePath.contains(spring)) {
1162                sourcePath.clear();
1163                while (spring != null) {
1164                    if (spring instanceof ParallelGroup) {
1165                        return true;
1166                    }
1167                    spring = spring.getParent();
1168                }
1169                return false;
1170            }
1171            spring = spring.getParent();
1172        }
1173        sourcePath.clear();
1174        return false;
1175    }
1176    
1177    private boolean isLeftToRight() {
1178        return host.getComponentOrientation().isLeftToRight();
1179    }
1180    
1181    /**
1182     * Returns a string representation of this {@code GroupLayout}.
1183     * This method is intended to be used for debugging purposes,
1184     * and the content and format of the returned string may vary
1185     * between implementations.
1186     *
1187     * @return a string representation of this {@code GroupLayout}
1188     **/

1189    public String JavaDoc toString() {
1190        if (springsChanged) {
1191            registerComponents(horizontalGroup, HORIZONTAL);
1192            registerComponents(verticalGroup, VERTICAL);
1193        }
1194        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1195        buffer.append("HORIZONTAL\n");
1196        createSpringDescription(buffer, horizontalGroup, " ", HORIZONTAL);
1197        buffer.append("\nVERTICAL\n");
1198        createSpringDescription(buffer, verticalGroup, " ", VERTICAL);
1199        return buffer.toString();
1200    }
1201    
1202    private void createSpringDescription(StringBuffer JavaDoc buffer, Spring JavaDoc spring,
1203            String JavaDoc indent, int axis) {
1204        String JavaDoc origin = "";
1205        String JavaDoc padding = "";
1206        if (spring instanceof ComponentSpring) {
1207            ComponentSpring cSpring = (ComponentSpring)spring;
1208            origin = Integer.toString(cSpring.getOrigin()) + " ";
1209            String JavaDoc name = cSpring.getComponent().getName();
1210            if (name != null) {
1211                origin = "name=" + name + ", ";
1212            }
1213        }
1214        if (spring instanceof AutoPreferredGapSpring) {
1215            AutoPreferredGapSpring paddingSpring =
1216                    (AutoPreferredGapSpring)spring;
1217            padding = ", userCreated=" + paddingSpring.getUserCreated() +
1218                    ", matches=" + paddingSpring.getMatchDescription();
1219        }
1220        buffer.append(indent + spring.getClass().getName() + " " +
1221                Integer.toHexString(spring.hashCode()) + " " +
1222                origin +
1223                ", size=" + spring.getSize() +
1224                ", alignment=" + spring.getAlignment() +
1225                " prefs=[" + spring.getMinimumSize(axis) +
1226                " " + spring.getPreferredSize(axis) +
1227                " " + spring.getMaximumSize(axis) +
1228                padding + "]\n");
1229        if (spring instanceof Group) {
1230            List<Spring JavaDoc> springs = ((Group)spring).springs;
1231            indent += " ";
1232            for (int counter = 0; counter < springs.size(); counter++) {
1233                createSpringDescription(buffer, springs.get(counter), indent,
1234                        axis);
1235            }
1236        }
1237    }
1238    
1239    
1240    /**
1241     * Spring consists of a range: min, pref and max, a value some where in
1242     * the middle of that, and a location. Spring caches the
1243     * min/max/pref. If the min/pref/max has internally changes, or needs
1244     * to be updated you must invoke clear.
1245     */

1246    private abstract class Spring {
1247        private int size;
1248        private int min;
1249        private int max;
1250        private int pref;
1251        private Spring JavaDoc parent;
1252        
1253        private Alignment alignment;
1254        
1255        Spring() {
1256            min = pref = max = UNSET;
1257        }
1258        
1259        /**
1260         * Calculates and returns the minimum size.
1261         *
1262         * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
1263         * @return the minimum size
1264         */

1265        abstract int calculateMinimumSize(int axis);
1266        
1267        /**
1268         * Calculates and returns the preferred size.
1269         *
1270         * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
1271         * @return the preferred size
1272         */

1273        abstract int calculatePreferredSize(int axis);
1274        
1275        /**
1276         * Calculates and returns the minimum size.
1277         *
1278         * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
1279         * @return the minimum size
1280         */

1281        abstract int calculateMaximumSize(int axis);
1282        
1283        /**
1284         * Sets the parent of this Spring.
1285         */

1286        void setParent(Spring JavaDoc parent) {
1287            this.parent = parent;
1288        }
1289        
1290        /**
1291         * Returns the parent of this spring.
1292         */

1293        Spring JavaDoc getParent() {
1294            return parent;
1295        }
1296        
1297        // This is here purely as a conveniance for ParallelGroup to avoid
1298
// having to track alignment separately.
1299
void setAlignment(Alignment alignment) {
1300            this.alignment = alignment;
1301        }
1302        
1303        /**
1304         * Alignment for this Spring, this may be null.
1305         */

1306        Alignment getAlignment() {
1307            return alignment;
1308        }
1309        
1310        /**
1311         * Returns the minimum size.
1312         */

1313        final int getMinimumSize(int axis) {
1314            if (min == UNSET) {
1315                min = constrain(calculateMinimumSize(axis));
1316            }
1317            return min;
1318        }
1319        
1320        /**
1321         * Returns the preferred size.
1322         */

1323        final int getPreferredSize(int axis) {
1324            if (pref == UNSET) {
1325                pref = constrain(calculatePreferredSize(axis));
1326            }
1327            return pref;
1328        }
1329        
1330        /**
1331         * Returns the maximum size.
1332         */

1333        final int getMaximumSize(int axis) {
1334            if (max == UNSET) {
1335                max = constrain(calculateMaximumSize(axis));
1336            }
1337            return max;
1338        }
1339        
1340        /**
1341         * Sets the value and location of the spring. Subclasses
1342         * will want to invoke super, then do any additional sizing.
1343         *
1344         * @param axis HORIZONTAL or VERTICAL
1345         * @param origin of this Spring
1346         * @param size of the Spring. If size is UNSET, this invokes
1347         * clear.
1348         */

1349        void setSize(int axis, int origin, int size) {
1350            this.size = size;
1351            if (size == UNSET) {
1352                unset();
1353            }
1354        }
1355        
1356        /**
1357         * Resets the cached min/max/pref.
1358         */

1359        void unset() {
1360            size = min = pref = max = UNSET;
1361        }
1362        
1363        /**
1364         * Returns the current size.
1365         */

1366        int getSize() {
1367            return size;
1368        }
1369        
1370        int constrain(int value) {
1371            return Math.min(value, Short.MAX_VALUE);
1372        }
1373        
1374        int getBaseline() {
1375            return -1;
1376        }
1377        
1378        BaselineResizeBehavior getBaselineResizeBehavior() {
1379            return BaselineResizeBehavior.OTHER;
1380        }
1381        
1382        final boolean isResizable(int axis) {
1383            int min = getMinimumSize(axis);
1384            int pref = getPreferredSize(axis);
1385            return (min != pref || pref != getMaximumSize(axis));
1386        }
1387    }
1388    
1389    /**
1390     * {@code Group} provides the basis for the two types of
1391     * operations supported by {@code GroupLayout}: laying out
1392     * components one after another ({@link SequentialGroup SequentialGroup})
1393     * or aligned ({@link ParallelGroup ParallelGroup}). {@code Group} and
1394     * its subclasses have no public constructor; to create one use
1395     * one of {@code createSequentialGroup} or
1396     * {@code createParallelGroup}. Additionally, taking a {@code Group}
1397     * created from one {@code GroupLayout} and using it with another
1398     * will produce undefined results.
1399     * <p>
1400     * Various methods in {@code Group} and its subclasses allow you
1401     * to explicitly specify the range. The arguments to these methods
1402     * can take two forms, either a value greater than or equal to 0,
1403     * or one of {@code DEFAULT_SIZE} or {@code PREFERRED_SIZE}. A
1404     * value greater than or equal to {@code 0} indicates a specific
1405     * size. {@code DEFAULT_SIZE} indicates the corresponding size
1406     * from the component should be used. For example, if {@code
1407     * DEFAULT_SIZE} is passed as the minimum size argument, the
1408     * minimum size is obtained from invoking {@code getMinimumSize}
1409     * on the component. Likewise, {@code PREFERRED_SIZE} indicates
1410     * the value from {@code getPreferredSize} should be used.
1411     * The following example adds {@code myComponent} to {@code group}
1412     * with specific values for the range. That is, the minimum is
1413     * explicitly specified as 100, preferred as 200, and maximum as
1414     * 300.
1415     * <pre>
1416     * group.addComponent(myComponent, 100, 200, 300);
1417     * </pre>
1418     * The following example adds {@code myComponent} to {@code group} using
1419     * a combination of the forms. The minimum size is forced to be the
1420     * same as the preferred size, the preferred size is determined by
1421     * using {@code myComponent.getPreferredSize} and the maximum is
1422     * determined by invoking {@code getMaximumSize} on the component.
1423     * <pre>
1424     * group.addComponent(myComponent, GroupLayout.PREFERRED_SIZE,
1425     * GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE);
1426     * </pre>
1427     * <p>
1428     * Unless otherwise specified all the methods of {@code Group} and
1429     * its subclasses that allow you to specify a range throw an
1430     * {@code IllegalArgumentException} if passed an invalid range. An
1431     * invalid range is one in which any of the values are &lt; 0 and
1432     * not one of {@code PREFERRED_SIZE} or {@code DEFAULT_SIZE}, or
1433     * the following is not met (for specific values): {@code min}
1434     * &lt;= {@code pref} &lt;= {@code max}.
1435     * <p>
1436     * Similarly any methods that take a {@code Component} throw a
1437     * {@code NullPointerException} if passed {@code null} and any methods
1438     * that take a {@code Group} throw an {@code IllegalArgumentException} if
1439     * passed {@code null}.
1440     *
1441     * @see #createSequentialGroup
1442     * @see #createParallelGroup
1443     * @since 1.6
1444     */

1445    public abstract class Group extends Spring JavaDoc {
1446        // private int origin;
1447
// private int size;
1448
List<Spring JavaDoc> springs;
1449        
1450        Group() {
1451            springs = new ArrayList<Spring JavaDoc>();
1452        }
1453        
1454        /**
1455         * Adds a {@code Group} to this {@code Group}.
1456         *
1457         * @param group the {@code Group} to add
1458         * @return this {@code Group}
1459         */

1460        public Group addGroup(Group group) {
1461            return addSpring(group);
1462        }
1463        
1464        /**
1465         * Adds a {@code Component} to this {@code Group}.
1466         *
1467         * @param component the {@code Component} to add
1468         * @return this {@code Group}
1469         */

1470        public Group addComponent(Component JavaDoc component) {
1471            return addComponent(component, DEFAULT_SIZE, DEFAULT_SIZE,
1472                    DEFAULT_SIZE);
1473        }
1474        
1475        /**
1476         * Adds a {@code Component} to this {@code Group}
1477         * with the specified size.
1478         *
1479         * @param component the {@code Component} to add
1480         * @param min the minimum size or one of {@code DEFAULT_SIZE} or
1481         * {@code PREFERRED_SIZE}
1482         * @param pref the preferred size or one of {@code DEFAULT_SIZE} or
1483         * {@code PREFERRED_SIZE}
1484         * @param max the maximum size or one of {@code DEFAULT_SIZE} or
1485         * {@code PREFERRED_SIZE}
1486         * @return this {@code Group}
1487         */

1488        public Group addComponent(Component JavaDoc component, int min, int pref,
1489                int max) {
1490            return addSpring(new ComponentSpring(component, min, pref, max));
1491        }
1492        
1493        /**
1494         * Adds a rigid gap to this {@code Group}.
1495         *
1496         * @param size the size of the gap
1497         * @return this {@code Group}
1498         * @throws IllegalArgumentException if {@code size} is less than
1499         * {@code 0}
1500         */

1501        public Group addGap(int size) {
1502            return addGap(size, size, size);
1503        }
1504        
1505        /**
1506         * Adds a gap to this {@code Group} with the specified size.
1507         *
1508         * @param min the minimum size of the gap
1509         * @param pref the preferred size of the gap
1510         * @param max the maximum size of the gap
1511         * @throws IllegalArgumentException if any of the values are
1512         * less than {@code 0}
1513         * @return this {@code Group}
1514         */

1515        public Group addGap(int min, int pref, int max) {
1516            return addSpring(new GapSpring(min, pref, max));
1517        }
1518        
1519        Spring JavaDoc getSpring(int index) {
1520            return springs.get(index);
1521        }
1522        
1523        int indexOf(Spring JavaDoc spring) {
1524            return springs.indexOf(spring);
1525        }
1526        
1527        /**
1528         * Adds the Spring to the list of {@code Spring}s and returns
1529         * the receiver.
1530         */

1531        Group addSpring(Spring JavaDoc spring) {
1532            springs.add(spring);
1533            spring.setParent(this);
1534            if (!(spring instanceof AutoPreferredGapSpring) ||
1535                    !((AutoPreferredGapSpring)spring).getUserCreated()) {
1536                springsChanged = true;
1537            }
1538            return this;
1539        }
1540        
1541        //
1542
// Spring methods
1543
//
1544

1545        void setSize(int axis, int origin, int size) {
1546            super.setSize(axis, origin, size);
1547            if (size == UNSET) {
1548                for (int counter = springs.size() - 1; counter >= 0;
1549                counter--) {
1550                    getSpring(counter).setSize(axis, origin, size);
1551                }
1552            } else {
1553                setValidSize(axis, origin, size);
1554            }
1555        }
1556        
1557        /**
1558         * This is invoked from {@code setSize} if passed a value
1559         * other than UNSET.
1560         */

1561        abstract void setValidSize(int axis, int origin, int size);
1562        
1563        int calculateMinimumSize(int axis) {
1564            return calculateSize(axis, MIN_SIZE);
1565        }
1566        
1567        int calculatePreferredSize(int axis) {
1568            return calculateSize(axis, PREF_SIZE);
1569        }
1570        
1571        int calculateMaximumSize(int axis) {
1572            return calculateSize(axis, MAX_SIZE);
1573        }
1574        
1575        /**
1576         * Calculates the specified size. This is called from
1577         * one of the {@code getMinimumSize0},
1578         * {@code getPreferredSize0} or
1579         * {@code getMaximumSize0} methods. This will invoke
1580         * to {@code operator} to combine the values.
1581         */

1582        int calculateSize(int axis, int type) {
1583            int count = springs.size();
1584            if (count == 0) {
1585                return 0;
1586            }
1587            if (count == 1) {
1588                return getSpringSize(getSpring(0), axis, type);
1589            }
1590            int size = constrain(operator(getSpringSize(getSpring(0), axis,
1591                    type), getSpringSize(getSpring(1), axis, type)));
1592            for (int counter = 2; counter < count; counter++) {
1593                size = constrain(operator(size, getSpringSize(
1594                        getSpring(counter), axis, type)));
1595            }
1596            return size;
1597        }
1598        
1599        int getSpringSize(Spring JavaDoc spring, int axis, int type) {
1600            switch(type) {
1601                case MIN_SIZE:
1602                    return spring.getMinimumSize(axis);
1603                case PREF_SIZE:
1604                    return spring.getPreferredSize(axis);
1605                case MAX_SIZE:
1606                    return spring.getMaximumSize(axis);
1607            }
1608            assert false;
1609            return 0;
1610        }
1611        
1612        /**
1613         * Used to compute how the two values representing two springs
1614         * will be combined. For example, a group that layed things out
1615         * one after the next would return {@code a + b}.
1616         */

1617        abstract int operator(int a, int b);
1618        
1619        //
1620
// Padding
1621
//
1622

1623        /**
1624         * Adjusts the autopadding springs in this group and its children.
1625         * If {@code insert} is true this will insert auto padding
1626         * springs, otherwise this will only adjust the springs that
1627         * comprise auto preferred padding springs.
1628         *
1629         * @param axis the axis of the springs; HORIZONTAL or VERTICAL
1630         * @param leadingPadding List of AutopaddingSprings that occur before
1631         * this Group
1632         * @param trailingPadding any trailing autopadding springs are added
1633         * to this on exit
1634         * @param leading List of ComponentSprings that occur before this Group
1635         * @param trailing any trailing ComponentSpring are added to this
1636         * List
1637         * @param insert Whether or not to insert AutopaddingSprings or just
1638         * adjust any existing AutopaddingSprings.
1639         */

1640        abstract void insertAutopadding(int axis,
1641                List<AutoPreferredGapSpring> leadingPadding,
1642                List<AutoPreferredGapSpring> trailingPadding,
1643                List<ComponentSpring> leading, List<ComponentSpring> trailing,
1644                boolean insert);
1645        
1646        /**
1647         * Removes any AutopaddingSprings for this Group and its children.
1648         */

1649        void removeAutopadding() {
1650            unset();
1651            for (int counter = springs.size() - 1; counter >= 0; counter--) {
1652                Spring JavaDoc spring = springs.get(counter);
1653                if (spring instanceof AutoPreferredGapSpring) {
1654                    if (((AutoPreferredGapSpring)spring).getUserCreated()) {
1655                        ((AutoPreferredGapSpring)spring).reset();
1656                    } else {
1657                        springs.remove(counter);
1658                    }
1659                } else if (spring instanceof Group) {
1660                    ((Group)spring).removeAutopadding();
1661                }
1662            }
1663        }
1664        
1665        void unsetAutopadding() {
1666            // Clear cached pref/min/max.
1667
unset();
1668            for (int counter = springs.size() - 1; counter >= 0; counter--) {
1669                Spring JavaDoc spring = springs.get(counter);
1670                if (spring instanceof AutoPreferredGapSpring) {
1671                    ((AutoPreferredGapSpring)spring).unset();
1672                } else if (spring instanceof Group) {
1673                    ((Group)spring).unsetAutopadding();
1674                }
1675            }
1676        }
1677        
1678        void calculateAutopadding(int axis) {
1679            for (int counter = springs.size() - 1; counter >= 0; counter--) {
1680                Spring JavaDoc spring = springs.get(counter);
1681                if (spring instanceof AutoPreferredGapSpring) {
1682                    // Force size to be reset.
1683
spring.unset();
1684                    ((AutoPreferredGapSpring)spring).calculatePadding(axis);
1685                } else if (spring instanceof Group) {
1686                    ((Group)spring).calculateAutopadding(axis);
1687                }
1688            }
1689            // Clear cached pref/min/max.
1690
unset();
1691        }
1692    }
1693    
1694    
1695    /**
1696     * A {@code Group} that positions and sizes its elements
1697     * sequentially, one after another. This class has no public
1698     * constructor, use the {@code createSequentialGroup} method
1699     * to create one.
1700     * <p>
1701     * In order to align a {@code SequentialGroup} along the baseline
1702     * of a baseline aligned {@code ParallelGroup} you need to specify
1703     * which of the elements of the {@code SequentialGroup} is used to
1704     * determine the baseline. The element used to calculate the
1705     * baseline is specified using one of the {@code add} methods that
1706     * take a {@code boolean}. The last element added with a value of
1707     * {@code true} for {@code useAsBaseline} is used to calculate the
1708     * baseline.
1709     *
1710     * @see #createSequentialGroup
1711     * @since 1.6
1712     */

1713    public class SequentialGroup extends Group {
1714        private Spring JavaDoc baselineSpring;
1715        
1716        SequentialGroup() {
1717        }
1718        
1719        /**
1720         * {@inheritDoc}
1721         */

1722        public SequentialGroup addGroup(Group group) {
1723            return (SequentialGroup)super.addGroup(group);
1724        }
1725
1726        /**
1727         * Adds a {@code Group} to this {@code Group}.
1728         *
1729         * @param group the {@code Group} to add
1730         * @param useAsBaseline whether the specified {@code Group} should
1731         * be used to calculate the baseline for this {@code Group}
1732         * @return this {@code Group}
1733         */

1734        public SequentialGroup addGroup(boolean useAsBaseline, Group group) {
1735            super.addGroup(group);
1736            if (useAsBaseline) {
1737                baselineSpring = group;
1738            }
1739            return this;
1740        }
1741        
1742        /**
1743         * {@inheritDoc}
1744         */

1745        public SequentialGroup addComponent(Component JavaDoc component) {
1746            return (SequentialGroup)super.addComponent(component);
1747        }
1748        
1749        /**
1750         * Adds a {@code Component} to this {@code Group}.
1751         *
1752         * @param useAsBaseline whether the specified {@code Component} should
1753         * be used to calculate the baseline for this {@code Group}
1754         * @param component the {@code Component} to add
1755         * @return this {@code Group}
1756         */

1757        public SequentialGroup addComponent(boolean useAsBaseline,
1758                Component JavaDoc component) {
1759            super.addComponent(component);
1760            if (useAsBaseline) {
1761                baselineSpring = springs.get(springs.size() - 1);
1762            }
1763            return this;
1764        }
1765
1766        /**
1767         * {@inheritDoc}
1768         */

1769        public SequentialGroup addComponent(Component JavaDoc component, int min,
1770                int pref, int max) {
1771            return (SequentialGroup)super.addComponent(
1772                    component, min, pref, max);
1773        }
1774        
1775        /**
1776         * Adds a {@code Component} to this {@code Group}
1777         * with the specified size.
1778         *
1779         * @param useAsBaseline whether the specified {@code Component} should
1780         * be used to calculate the baseline for this {@code Group}
1781         * @param component the {@code Component} to add
1782         * @param min the minimum size or one of {@code DEFAULT_SIZE} or
1783         * {@code PREFERRED_SIZE}
1784         * @param pref the preferred size or one of {@code DEFAULT_SIZE} or
1785         * {@code PREFERRED_SIZE}
1786         * @param max the maximum size or one of {@code DEFAULT_SIZE} or
1787         * {@code PREFERRED_SIZE}
1788         * @return this {@code Group}
1789         */

1790        public SequentialGroup addComponent(boolean useAsBaseline,
1791                Component JavaDoc component, int min, int pref, int max) {
1792            super.addComponent(component, min, pref, max);
1793            if (useAsBaseline) {
1794                baselineSpring = springs.get(springs.size() - 1);
1795            }
1796            return this;
1797        }
1798
1799        /**
1800         * {@inheritDoc}
1801         */

1802        public SequentialGroup addGap(int size) {
1803            return (SequentialGroup)super.addGap(size);
1804        }
1805        
1806        /**
1807         * {@inheritDoc}
1808         */

1809        public SequentialGroup addGap(int min, int pref, int max) {
1810            return (SequentialGroup)super.addGap(min, pref, max);
1811        }
1812        
1813        /**
1814         * Adds an element representing the preferred gap between two
1815         * components. The element created to represent the gap is not
1816         * resizable.
1817         *
1818         * @param comp1 the first component
1819         * @param comp2 the second component
1820         * @param type the type of gap; one of the constants defined by
1821         * {@code LayoutStyle}
1822         * @return this {@code SequentialGroup}
1823         * @throws IllegalArgumentException if {@code type}, {@code comp1} or
1824         * {@code comp2} is {@code null}
1825         * @see LayoutStyle
1826         */

1827        public SequentialGroup addPreferredGap(JComponent JavaDoc comp1,
1828                JComponent JavaDoc comp2, ComponentPlacement type) {
1829            return addPreferredGap(comp1, comp2, type, DEFAULT_SIZE,
1830                    PREFERRED_SIZE);
1831        }
1832        
1833        /**
1834         * Adds an element representing the preferred gap between two
1835         * components.
1836         *
1837         * @param comp1 the first component
1838         * @param comp2 the second component
1839         * @param type the type of gap
1840         * @param pref the preferred size of the grap; one of
1841         * {@code DEFAULT_SIZE} or a value &gt;= 0
1842         * @param max the maximum size of the gap; one of
1843         * {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}
1844         * or a value &gt;= 0
1845         * @return this {@code SequentialGroup}
1846         * @throws IllegalArgumentException if {@code type}, {@code comp1} or
1847         * {@code comp2} is {@code null}
1848         * @see LayoutStyle
1849         */

1850        public SequentialGroup addPreferredGap(JComponent JavaDoc comp1,
1851                JComponent JavaDoc comp2, ComponentPlacement type, int pref,
1852                int max) {
1853            if (type == null) {
1854                throw new IllegalArgumentException JavaDoc("Type must be non-null");
1855            }
1856            if (comp1 == null || comp2 == null) {
1857                throw new IllegalArgumentException JavaDoc(
1858                        "Components must be non-null");
1859            }
1860            checkPreferredGapValues(pref, max);
1861            return (SequentialGroup)addSpring(new PreferredGapSpring(
1862                    comp1, comp2, type, pref, max));
1863        }
1864        
1865        /**
1866         * Adds an element representing the preferred gap between the
1867         * nearest components. During layout, neighboring
1868         * components are found, and the size of the added gap is set
1869         * based on the preferred gap between the components. If no
1870         * neighboring components are found the gap has a size of {@code 0}.
1871         * <p>
1872         * The element created to represent the gap is not
1873         * resizable.
1874         *
1875         * @param type the type of gap; one of
1876         * {@code LayoutStyle.ComponentPlacement.RELATED} or
1877         * {@code LayoutStyle.ComponentPlacement.UNRELATED}
1878         * @return this {@code SequentialGroup}
1879         * @see LayoutStyle
1880         * @throws IllegalArgumentException if {@code type} is not one of
1881         * {@code LayoutStyle.ComponentPlacement.RELATED} or
1882         * {@code LayoutStyle.ComponentPlacement.UNRELATED}
1883         */

1884        public SequentialGroup addPreferredGap(ComponentPlacement type) {
1885            return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);
1886        }
1887        
1888        /**
1889         * Adds an element representing the preferred gap between the
1890         * nearest components. During layout, neighboring
1891         * components are found, and the minimum of this
1892         * gap is set based on the size of the preferred gap between the
1893         * neighboring components. If no neighboring components are found the
1894         * minimum size is set to 0.
1895         *
1896         * @param type the type of gap; one of
1897         * {@code LayoutStyle.ComponentPlacement.RELATED} or
1898         * {@code LayoutStyle.ComponentPlacement.UNRELATED}
1899         * @param pref the preferred size of the grap; one of
1900         * {@code DEFAULT_SIZE} or a value &gt;= 0
1901         * @param max the maximum size of the gap; one of
1902         * {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}
1903         * or a value &gt;= 0
1904         * @return this {@code SequentialGroup}
1905         * @throws IllegalArgumentException if {@code type} is not one of
1906         * {@code LayoutStyle.ComponentPlacement.RELATED} or
1907         * {@code LayoutStyle.ComponentPlacement.UNRELATED}
1908         * @see LayoutStyle
1909         */

1910        public SequentialGroup addPreferredGap(ComponentPlacement type,
1911                int pref, int max) {
1912            if (type != ComponentPlacement.RELATED &&
1913                    type != ComponentPlacement.UNRELATED) {
1914                throw new IllegalArgumentException JavaDoc(
1915                        "Type must be one of " +
1916                        "LayoutStyle.ComponentPlacement.RELATED or " +
1917                        "LayoutStyle.ComponentPlacement.UNRELATED");
1918            }
1919            checkPreferredGapValues(pref, max);
1920            hasPreferredPaddingSprings = true;
1921            return (SequentialGroup)addSpring(new AutoPreferredGapSpring(
1922                    type, pref, max));
1923        }
1924        
1925        /**
1926         * Adds an element representing the preferred gap between an edge
1927         * the container and components that touch the border of the
1928         * container. This has no effect if the added gap does not
1929         * touch an edge of the parent container.
1930         * <p>
1931         * The element created to represent the gap is not
1932         * resizable.
1933         *
1934         * @return this {@code SequentialGroup}
1935         */

1936        public SequentialGroup addContainerGap() {
1937            return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);
1938        }
1939        
1940        /**
1941         * Adds an element representing the preferred gap between one
1942         * edge of the container and the next or previous {@code
1943         * Component} with the specified size. This has no
1944         * effect if the next or previous element is not a {@code
1945         * Component} and does not touch one edge of the parent
1946         * container.
1947         *
1948         * @param pref the preferred size; one of {@code DEFAULT_SIZE} or a
1949         * value &gt;= 0
1950         * @param max the maximum size; one of {@code DEFAULT_SIZE},
1951         * {@code PREFERRED_SIZE} or a value &gt;= 0
1952         * @return this {@code SequentialGroup}
1953         */

1954        public SequentialGroup addContainerGap(int pref, int max) {
1955            if ((pref < 0 && pref != DEFAULT_SIZE) ||
1956                    (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
1957                    (pref >= 0 && max >= 0 && pref > max)) {
1958                throw new IllegalArgumentException JavaDoc(
1959                        "Pref and max must be either DEFAULT_VALUE " +
1960                        "or >= 0 and pref <= max");
1961            }
1962            hasPreferredPaddingSprings = true;
1963            return (SequentialGroup)addSpring(
1964                    new ContainerAutoPreferredGapSpring(pref, max));
1965        }
1966        
1967        int operator(int a, int b) {
1968            return constrain(a) + constrain(b);
1969        }
1970        
1971        void setValidSize(int axis, int origin, int size) {
1972            int pref = getPreferredSize(axis);
1973            if (size == pref) {
1974                // Layout at preferred size
1975
for (Spring JavaDoc spring : springs) {
1976                    int springPref = spring.getPreferredSize(axis);
1977                    spring.setSize(axis, origin, springPref);
1978                    origin += springPref;
1979                }
1980            } else if (springs.size() == 1) {
1981                Spring JavaDoc spring = getSpring(0);
1982                spring.setSize(axis, origin, Math.min(
1983                        Math.max(size, spring.getMinimumSize(axis)),
1984                        spring.getMaximumSize(axis)));
1985            } else if (springs.size() > 1) {
1986                // Adjust between min/pref
1987
setValidSizeNotPreferred(axis, origin, size);
1988            }
1989        }
1990        
1991        private void setValidSizeNotPreferred(int axis, int origin, int size) {
1992            int delta = size - getPreferredSize(axis);
1993            assert delta != 0;
1994            boolean useMin = (delta < 0);
1995            int springCount = springs.size();
1996            if (useMin) {
1997                delta *= -1;
1998            }
1999            
2000            // The following algorithm if used for resizing springs:
2001
// 1. Calculate the resizability of each spring (pref - min or
2002
// max - pref) into a list.
2003
// 2. Sort the list in ascending order
2004
// 3. Iterate through each of the resizable Springs, attempting
2005
// to give them (pref - size) / resizeCount
2006
// 4. For any Springs that can not accomodate that much space
2007
// add the remainder back to the amount to distribute and
2008
// recalculate how must space the remaining springs will get.
2009
// 5. Set the size of the springs.
2010

2011            // First pass, sort the resizable springs into the List resizable
2012
List<SpringDelta> resizable = buildResizableList(axis, useMin);
2013            int resizableCount = resizable.size();
2014            
2015            if (resizableCount > 0) {
2016                // How much we would like to give each Spring.
2017
int sDelta = delta / resizableCount;
2018                // Remaining space.
2019
int slop = delta - sDelta * resizableCount;
2020                int[] sizes = new int[springCount];
2021                int sign = useMin ? -1 : 1;
2022                // Second pass, accumulate the resulting deltas (relative to
2023
// preferred) into sizes.
2024
for (int counter = 0; counter < resizableCount; counter++) {
2025                    SpringDelta springDelta = resizable.get(counter);
2026                    if ((counter + 1) == resizableCount) {
2027                        sDelta += slop;
2028                    }
2029                    springDelta.delta = Math.min(sDelta, springDelta.delta);
2030                    delta -= springDelta.delta;
2031                    if (springDelta.delta != sDelta && counter + 1 <
2032                            resizableCount) {
2033                        // Spring didn't take all the space, reset how much
2034
// each spring will get.
2035
sDelta = delta / (resizableCount - counter - 1);
2036                        slop = delta - sDelta * (resizableCount - counter - 1);
2037                    }
2038                    sizes[springDelta.index] = sign * springDelta.delta;
2039                }
2040                
2041                // And finally set the size of each spring
2042
for (int counter = 0; counter < springCount; counter++) {
2043                    Spring JavaDoc spring = getSpring(counter);
2044                    int sSize = spring.getPreferredSize(axis) + sizes[counter];
2045                    spring.setSize(axis, origin, sSize);
2046                    origin += sSize;
2047                }
2048            } else {
2049                // Nothing resizable, use the min or max of each of the
2050
// springs.
2051
for (int counter = 0; counter < springCount; counter++) {
2052                    Spring JavaDoc spring = getSpring(counter);
2053                    int sSize;
2054                    if (useMin) {
2055                        sSize = spring.getMinimumSize(axis);
2056                    } else {
2057                        sSize = spring.getMaximumSize(axis);
2058                    }
2059                    spring.setSize(axis, origin, sSize);
2060                    origin += sSize;
2061                }
2062            }
2063        }
2064        
2065        /**
2066         * Returns the sorted list of SpringDelta's for the current set of
2067         * Springs. The list is ordered based on the amount of flexibility of
2068         * the springs.
2069         */

2070        private List<SpringDelta> buildResizableList(int axis,
2071                boolean useMin) {
2072            // First pass, figure out what is resizable
2073
int size = springs.size();
2074            List<SpringDelta> sorted = new ArrayList<SpringDelta>(size);
2075            for (int counter = 0; counter < size; counter++) {
2076                Spring JavaDoc spring = getSpring(counter);
2077                int sDelta;
2078                if (useMin) {
2079                    sDelta = spring.getPreferredSize(axis) -
2080                            spring.getMinimumSize(axis);
2081                } else {
2082                    sDelta = spring.getMaximumSize(axis) -
2083                            spring.getPreferredSize(axis);
2084                }
2085                if (sDelta > 0) {
2086                    sorted.add(new SpringDelta(counter, sDelta));
2087                }
2088            }
2089            Collections.sort(sorted);
2090            return sorted;
2091        }
2092        
2093        void insertAutopadding(int axis,
2094                List<AutoPreferredGapSpring> leadingPadding,
2095                List<AutoPreferredGapSpring> trailingPadding,
2096                List<ComponentSpring> leading, List<ComponentSpring> trailing,
2097                boolean insert) {
2098            List<AutoPreferredGapSpring> newLeadingPadding =
2099                    new ArrayList<AutoPreferredGapSpring>(leadingPadding);
2100            List<AutoPreferredGapSpring> newTrailingPadding =
2101                    new ArrayList<AutoPreferredGapSpring>(1);
2102            List<ComponentSpring> newLeading =
2103                    new ArrayList<ComponentSpring>(leading);
2104            List<ComponentSpring> newTrailing = null;
2105            // Warning, this must use springs.size, as it may change during the
2106
// loop.
2107
for (int counter = 0; counter < springs.size(); counter++) {
2108                Spring JavaDoc spring = getSpring(counter);
2109                if (spring instanceof AutoPreferredGapSpring) {
2110                    // Autopadding spring. Set the sources of the
2111
// autopadding spring based on newLeading.
2112
AutoPreferredGapSpring padding =
2113                            (AutoPreferredGapSpring)spring;
2114                    padding.setSources(newLeading);
2115                    newLeading.clear();
2116                    if (counter + 1 == springs.size()) {
2117                        // Last spring in the list, add it to trailingPadding.
2118
if (!(padding instanceof
2119                                ContainerAutoPreferredGapSpring)) {
2120                            trailingPadding.add(padding);
2121                        }
2122                    } else {
2123                        newLeadingPadding.clear();
2124                        newLeadingPadding.add(padding);
2125                    }
2126                } else {
2127                    // Not a padding spring
2128
if (newLeading.size() > 0 && insert) {
2129                        // There's leading ComponentSprings, create an
2130
// autopadding spring.
2131
AutoPreferredGapSpring padding =
2132                                new AutoPreferredGapSpring();
2133                        // Force this to be revisted by decrementing counter
2134
// and breaking
2135
springs.add(counter--, padding);
2136                        continue;
2137                    }
2138                    if (spring instanceof ComponentSpring) {
2139                        // Spring is a Component, make it the target of any
2140
// leading AutopaddingSpring.
2141
ComponentSpring cSpring = (ComponentSpring)spring;
2142                        if (!cSpring.isVisible()) {
2143                            continue;
2144                        }
2145                        for (AutoPreferredGapSpring gapSpring : newLeadingPadding) {
2146                            gapSpring.addTarget(cSpring, axis);
2147                        }
2148                        newLeading.clear();
2149                        newLeadingPadding.clear();
2150                        if (counter + 1 == springs.size()) {
2151                            // Last Spring, add it to trailing
2152
trailing.add(cSpring);
2153                        } else {
2154                            // Not that last Spring, add it to leading
2155
newLeading.add(cSpring);
2156                        }
2157                    } else if (spring instanceof Group) {
2158                        // Forward call to child Group
2159
if (newTrailing == null) {
2160                            newTrailing = new ArrayList<ComponentSpring>(1);
2161                        } else {
2162                            newTrailing.clear();
2163                        }
2164                        newTrailingPadding.clear();
2165                        ((Group)spring).insertAutopadding(axis,
2166                                newLeadingPadding, newTrailingPadding,
2167                                newLeading, newTrailing, insert);
2168                        newLeading.clear();
2169                        newLeadingPadding.clear();
2170                        if (counter + 1 == springs.size()) {
2171                            trailing.addAll(newTrailing);
2172                            trailingPadding.addAll(newTrailingPadding);
2173                        } else {
2174                            newLeading.addAll(newTrailing);
2175                            newLeadingPadding.addAll(newTrailingPadding);
2176                        }
2177                    } else {
2178                        // Gap
2179
newLeadingPadding.clear();
2180                        newLeading.clear();
2181                    }
2182                }
2183            }
2184        }
2185        
2186        int getBaseline() {
2187            if (baselineSpring != null) {
2188                int baseline = baselineSpring.getBaseline();
2189                if (baseline >= 0) {
2190                    int size = 0;
2191                    for (Spring JavaDoc spring : springs) {
2192                        if (spring == baselineSpring) {
2193                            return size + baseline;
2194                        } else {
2195                            size += spring.getPreferredSize(VERTICAL);
2196                        }
2197                    }
2198                }
2199            }
2200            return -1;
2201        }
2202        
2203        BaselineResizeBehavior getBaselineResizeBehavior() {
2204            if (isResizable(VERTICAL)) {
2205                if (!baselineSpring.isResizable(VERTICAL)) {
2206                    // Spring to use for baseline isn't resizable. In this case
2207
// baseline resize behavior can be determined based on how
2208
// preceeding springs resize.
2209
boolean leadingResizable = false;
2210                    for (Spring JavaDoc spring : springs) {
2211                        if (spring == baselineSpring) {
2212                            break;
2213                        } else if (spring.isResizable(VERTICAL)) {
2214                            leadingResizable = true;
2215                            break;
2216                        }
2217                    }
2218                    boolean trailingResizable = false;
2219                    for (int i = springs.size() - 1; i >= 0; i--) {
2220                        Spring JavaDoc spring = springs.get(i);
2221                        if (spring == baselineSpring) {
2222                            break;
2223                        }
2224                        if (spring.isResizable(VERTICAL)) {
2225                            trailingResizable = true;
2226                            break;
2227                        }
2228                    }
2229                    if (leadingResizable && !trailingResizable) {
2230                        return BaselineResizeBehavior.CONSTANT_DESCENT;
2231                    } else if (!leadingResizable && trailingResizable) {
2232                        return BaselineResizeBehavior.CONSTANT_ASCENT;
2233                    }
2234                    // If we get here, both leading and trailing springs are
2235
// resizable. Fall through to OTHER.
2236
} else {
2237                    BaselineResizeBehavior brb = baselineSpring.getBaselineResizeBehavior();
2238                    if (brb == BaselineResizeBehavior.CONSTANT_ASCENT) {
2239                        for (Spring JavaDoc spring : springs) {
2240                            if (spring == baselineSpring) {
2241                                return BaselineResizeBehavior.CONSTANT_ASCENT;
2242                            }
2243                            if (spring.isResizable(VERTICAL)) {
2244                                return BaselineResizeBehavior.OTHER;
2245                            }
2246                        }
2247                    } else if (brb == BaselineResizeBehavior.CONSTANT_DESCENT) {
2248                        for (int i = springs.size() - 1; i >= 0; i--) {
2249                            Spring JavaDoc spring = springs.get(i);
2250                            if (spring == baselineSpring) {
2251                                return BaselineResizeBehavior.CONSTANT_DESCENT;
2252                            }
2253                            if (spring.isResizable(VERTICAL)) {
2254                                return BaselineResizeBehavior.OTHER;
2255                            }
2256                        }
2257                    }
2258                }
2259                return BaselineResizeBehavior.OTHER;
2260            }
2261            // Not resizable, treat as constant_ascent
2262
return BaselineResizeBehavior.CONSTANT_ASCENT;
2263        }
2264        
2265        private void checkPreferredGapValues(int pref, int max) {
2266            if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) ||
2267                    (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
2268                    (pref >= 0 && max >= 0 && pref > max)) {
2269                throw new IllegalArgumentException JavaDoc(
2270                        "Pref and max must be either DEFAULT_SIZE, " +
2271                        "PREFERRED_SIZE, or >= 0 and pref <= max");
2272            }
2273        }
2274    }
2275    
2276    
2277    /**
2278     * Used by SequentialGroup in calculating resizability of springs.
2279     */

2280    private static final class SpringDelta implements Comparable JavaDoc<SpringDelta> {
2281        // Original index.
2282
public final int index;
2283        // Delta, one of pref - min or max - pref.
2284
public int delta;
2285        
2286        public SpringDelta(int index, int delta) {
2287            this.index = index;
2288            this.delta = delta;
2289        }
2290        
2291        public int compareTo(SpringDelta o) {
2292            return delta - o.delta;
2293        }
2294        
2295        public String JavaDoc toString() {
2296            return super.toString() + "[index=" + index + ", delta=" +
2297                    delta + "]";
2298        }
2299    }
2300    
2301    
2302    /**
2303     * A {@code Group} that aligns and sizes it's children.
2304     * {@code ParallelGroup} aligns it's children in
2305     * four possible ways: along the baseline, centered, anchored to the
2306     * leading edge, or anchored to the trailing edge.
2307     * <h3>Baseline</h3>
2308     * A {@code ParallelGroup} that aligns it's children along the
2309     * baseline must first decide where the baseline is
2310     * anchored. The baseline can either be anchored to the top, or
2311     * anchored to the bottom of the group. That is, the distance between the
2312     * baseline and the beginning of the group can be a constant
2313     * distance, or the distance between the end of the group and the
2314     * baseline can be a constant distance. The possible choices
2315     * correspond to the {@code BaselineResizeBehavior} constants
2316     * {@link
2317     * java.awt.Component.BaselineResizeBehavior#CONSTANT_ASCENT CONSTANT_ASCENT} and
2318     * {@link
2319     * java.awt.Component.BaselineResizeBehavior#CONSTANT_DESCENT CONSTANT_DESCENT}.
2320     * <p>
2321     * The baseline anchor may be explicitly specified by the
2322     * {@code createBaselineGroup} method, or determined based on the elements.
2323     * If not explicitly specified, the baseline will be anchored to
2324     * the bottom if all the elements with a baseline, and that are
2325     * aligned to the baseline, have a baseline resize behavior of
2326     * {@code CONSTANT_DESCENT}; otherwise the baseline is anchored to the top
2327     * of the group.
2328     * <p>
2329     * Elements aligned to the baseline are resizable if they have have
2330     * a baseline resize behavior of {@code CONSTANT_ASCENT} or
2331     * {@code CONSTANT_DESCENT}. Elements with a baseline resize
2332     * behavior of {@code OTHER} or {@code CENTER_OFFSET} are not resizable.
2333     * <p>
2334     * The baseline is calculated based on the preferred height of each
2335     * of the elements that have a baseline. The baseline is
2336     * calculated using the following algorithm:
2337     * {@code max(maxNonBaselineHeight, maxAscent + maxDescent)}, where the
2338     * {@code maxNonBaselineHeight} is the maximum height of all elements
2339     * that do not have a baseline, or are not aligned along the baseline.
2340     * {@code maxAscent} is the maximum ascent (baseline) of all elements that
2341     * have a baseline and are aligned along the baseline.
2342     * {@code maxDescent} is the maximum descent (preferred height - baseline)
2343     * of all elements that have a baseline and are aligned along the baseline.
2344     * <p>
2345     * A {@code ParallelGroup} that aligns it's elements along the baseline
2346     * is only useful along the vertical axis. If you create a
2347     * baseline group and use it along the horizontal axis an
2348     * {@code IllegalStateException} is thrown when you ask
2349     * {@code GroupLayout} for the minimum, preferred or maximum size or
2350     * attempt to layout the components.
2351     * <p>
2352     * Elements that are not aligned to the baseline and smaller than the size
2353     * of the {@code ParallelGroup} are positioned in one of three
2354     * ways: centered, anchored to the leading edge, or anchored to the
2355     * trailing edge.
2356     *
2357     * <h3>Non-baseline {@code ParallelGroup}</h3>
2358     * {@code ParallelGroup}s created with an alignment other than
2359     * {@code BASELINE} align elements that are smaller than the size
2360     * of the group in one of three ways: centered, anchored to the
2361     * leading edge, or anchored to the trailing edge.
2362     * <p>
2363     * The leading edge is based on the axis and {@code
2364     * ComponentOrientation}. For the vertical axis the top edge is
2365     * always the leading edge, and the bottom edge is always the
2366     * trailing edge. When the {@code ComponentOrientation} is {@code
2367     * LEFT_TO_RIGHT}, the leading edge is the left edge and the
2368     * trailing edge the right edge. A {@code ComponentOrientation} of
2369     * {@code RIGHT_TO_LEFT} flips the left and right edges. Child
2370     * elements are aligned based on the specified alignment the
2371     * element was added with. If you do not specify an alignment, the
2372     * alignment specified for the {@code ParallelGroup} is used.
2373     * <p>
2374     * To align elements along the baseline you {@code createBaselineGroup},
2375     * or {@code createParallelGroup} with an alignment of {@code BASELINE}.
2376     * If the group was not created with a baseline alignment, and you attempt
2377     * to add an element specifying a baseline alignment, an
2378     * {@code IllegalArgumentException} is thrown.
2379     *
2380     * @see #createParallelGroup()
2381     * @see #createBaselineGroup(boolean,boolean)
2382     * @since 1.6
2383     */

2384    public class ParallelGroup extends Group {
2385        // How children are layed out.
2386
private final Alignment childAlignment;
2387        // Whether or not we're resizable.
2388
private final boolean resizable;
2389        
2390        ParallelGroup(Alignment childAlignment, boolean resizable) {
2391            this.childAlignment = childAlignment;
2392            this.resizable = resizable;
2393        }
2394        
2395        /**
2396         * {@inheritDoc}
2397         */

2398        public ParallelGroup addGroup(Group group) {
2399            return (ParallelGroup)super.addGroup(group);
2400        }
2401        
2402        /**
2403         * {@inheritDoc}
2404         */

2405        public ParallelGroup addComponent(Component JavaDoc component) {
2406            return (ParallelGroup)super.addComponent(component);
2407        }
2408        
2409        /**
2410         * {@inheritDoc}
2411         */

2412        public ParallelGroup addComponent(Component JavaDoc component, int min, int pref,
2413                int max) {
2414            return (ParallelGroup)super.addComponent(component, min, pref, max);
2415        }
2416        
2417        /**
2418         * {@inheritDoc}
2419         */

2420        public ParallelGroup addGap(int pref) {
2421            return (ParallelGroup)super.addGap(pref);
2422        }
2423        
2424        /**
2425         * {@inheritDoc}
2426         */

2427        public ParallelGroup addGap(int min, int pref, int max) {
2428            return (ParallelGroup)super.addGap(min, pref, max);
2429        }
2430        
2431        /**
2432         * Adds a {@code Group} to this {@code ParallelGroup} with the
2433         * specified alignment. If the child is smaller than the
2434         * {@code Group} it is aligned based on the specified
2435         * alignment.
2436         *
2437         * @param alignment the alignment
2438         * @param group the {@code Group} to add
2439         * @return this {@code ParallelGroup}
2440         * @throws IllegalArgumentException if {@code alignment} is
2441         * {@code null}
2442         */

2443        public ParallelGroup addGroup(Alignment alignment, Group group) {
2444            checkChildAlignment(alignment);
2445            group.setAlignment(alignment);
2446            return (ParallelGroup)addSpring(group);
2447        }
2448        
2449        /**
2450         * Adds a {@code Component} to this {@code ParallelGroup} with
2451         * the specified alignment.
2452         *
2453         * @param alignment the alignment
2454         * @param component the {@code Component} to add
2455         * @return this {@code Group}
2456         * @throws IllegalArgumentException if {@code alignment} is
2457         * {@code null}
2458         */

2459        public ParallelGroup addComponent(Component JavaDoc component,
2460                Alignment alignment) {
2461            return addComponent(component, alignment, DEFAULT_SIZE, DEFAULT_SIZE,
2462                    DEFAULT_SIZE);
2463        }
2464        
2465        /**
2466         * Adds a {@code Component} to this {@code ParallelGroup} with the
2467         * specified alignment and size.
2468         *
2469         * @param alignment the alignment
2470         * @param component the {@code Component} to add
2471         * @param min the minimum size
2472         * @param pref the preferred size
2473         * @param max the maximum size
2474         * @throws IllegalArgumentException if {@code alignment} is
2475         * {@code null}
2476         * @return this {@code Group}
2477         */

2478        public ParallelGroup addComponent(Component JavaDoc component,
2479                Alignment alignment, int min, int pref, int max) {
2480            checkChildAlignment(alignment);
2481            ComponentSpring spring = new ComponentSpring(component,
2482                    min, pref, max);
2483            spring.setAlignment(alignment);
2484            return (ParallelGroup)addSpring(spring);
2485        }
2486        
2487        boolean isResizable() {
2488            return resizable;
2489        }
2490        
2491        int operator(int a, int b) {
2492            return Math.max(a, b);
2493        }
2494        
2495        int calculateMinimumSize(int axis) {
2496            if (!isResizable()) {
2497                return getPreferredSize(axis);
2498            }
2499            return super.calculateMinimumSize(axis);
2500        }
2501        
2502        int calculateMaximumSize(int axis) {
2503            if (!isResizable()) {
2504                return getPreferredSize(axis);
2505            }
2506            return super.calculateMaximumSize(axis);
2507        }
2508        
2509        void setValidSize(int axis, int origin, int size) {
2510            for (Spring JavaDoc spring : springs) {
2511                setChildSize(spring, axis, origin, size);
2512            }
2513        }
2514        
2515        void setChildSize(Spring JavaDoc spring, int axis, int origin, int size) {
2516            Alignment alignment = spring.getAlignment();
2517            int springSize = Math.min(
2518                    Math.max(spring.getMinimumSize(axis), size),
2519                    spring.getMaximumSize(axis));
2520            if (alignment == null) {
2521                alignment = childAlignment;
2522            }
2523            switch (alignment) {
2524                case TRAILING:
2525                    spring.setSize(axis, origin + size - springSize,
2526                            springSize);
2527                    break;
2528                case CENTER:
2529                    spring.setSize(axis, origin +
2530                            (size - springSize) / 2,springSize);
2531                    break;
2532                default: // LEADING, or BASELINE
2533
spring.setSize(axis, origin, springSize);
2534                    break;
2535            }
2536        }
2537        
2538        void insertAutopadding(int axis,
2539                List<AutoPreferredGapSpring> leadingPadding,
2540                List<AutoPreferredGapSpring> trailingPadding,
2541                List<ComponentSpring> leading, List<ComponentSpring> trailing,
2542                boolean insert) {
2543            for (Spring JavaDoc spring : springs) {
2544                if (spring instanceof ComponentSpring) {
2545                    for (AutoPreferredGapSpring gapSpring : leadingPadding) {
2546                        gapSpring.addTarget((ComponentSpring)spring, axis);
2547                    }
2548                    trailing.add((ComponentSpring)spring);
2549                } else if (spring instanceof Group) {
2550                    ((Group)spring).insertAutopadding(axis, leadingPadding,
2551                            trailingPadding, leading, trailing, insert);
2552                } else if (spring instanceof AutoPreferredGapSpring) {
2553                    trailingPadding.add((AutoPreferredGapSpring)spring);
2554                }
2555            }
2556        }
2557        
2558        private void checkChildAlignment(Alignment alignment) {
2559            checkChildAlignment(alignment, (this instanceof BaselineGroup));
2560        }
2561        
2562        private void checkChildAlignment(Alignment alignment,
2563                boolean allowsBaseline) {
2564            if (alignment == null) {
2565                throw new IllegalArgumentException JavaDoc("Alignment must be non-null");
2566            }
2567            if (!allowsBaseline && alignment == Alignment.BASELINE) {
2568                throw new IllegalArgumentException JavaDoc("Alignment must be one of:" +
2569                        "LEADING, TRAILING or CENTER");
2570            }
2571        }
2572    }
2573    
2574    
2575    /**
2576     * An extension of {@code ParallelGroup} that aligns its
2577     * constituent {@code Spring}s along the baseline.
2578     */

2579    private class BaselineGroup extends ParallelGroup {
2580        // Whether or not all child springs have a baseline
2581
private boolean allSpringsHaveBaseline;
2582
2583        // max(spring.getBaseline()) of all springs aligned along the baseline
2584
// that have a baseline
2585
private int prefAscent;
2586
2587        // max(spring.getPreferredSize().height - spring.getBaseline()) of all
2588
// springs aligned along the baseline that have a baseline
2589
private int prefDescent;
2590
2591        // Whether baselineAnchoredToTop was explicitly set
2592
private boolean baselineAnchorSet;
2593
2594        // Whether the baseline is anchored to the top or the bottom.
2595
// If anchored to the top the baseline is always at prefAscent,
2596
// otherwise the baseline is at (height - prefDescent)
2597
private boolean baselineAnchoredToTop;
2598        
2599        // Whether or not the baseline has been calculated.
2600
private boolean calcedBaseline;
2601        
2602        BaselineGroup(boolean resizable) {
2603            super(Alignment.LEADING, resizable);
2604            prefAscent = prefDescent = -1;
2605            calcedBaseline = false;
2606        }
2607        
2608        BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) {
2609            this(resizable);
2610            this.baselineAnchoredToTop = baselineAnchoredToTop;
2611            baselineAnchorSet = true;
2612        }
2613        
2614        void unset() {
2615            super.unset();
2616            prefAscent = prefDescent = -1;
2617            calcedBaseline = false;
2618        }
2619        
2620        void setValidSize(int axis, int origin, int size) {
2621            checkAxis(axis);
2622            if (prefAscent == -1) {
2623                super.setValidSize(axis, origin, size);
2624            } else {
2625                // do baseline layout
2626
baselineLayout(origin, size);
2627            }
2628        }
2629        
2630        int calculateSize(int axis, int type) {
2631            checkAxis(axis);
2632            if (!calcedBaseline) {
2633                calculateBaselineAndResizeBehavior();
2634            }
2635            if (type == MIN_SIZE) {
2636                return calculateMinSize();
2637            }
2638            if (type == MAX_SIZE) {
2639                return calculateMaxSize();
2640            }
2641            if (allSpringsHaveBaseline) {
2642                return prefAscent + prefDescent;
2643            }
2644            return Math.max(prefAscent + prefDescent,
2645                    super.calculateSize(axis, type));
2646        }
2647        
2648        private void calculateBaselineAndResizeBehavior() {
2649            // calculate baseline
2650
prefAscent = 0;
2651            prefDescent = 0;
2652            int baselineSpringCount = 0;
2653            BaselineResizeBehavior resizeBehavior = null;
2654            for (Spring JavaDoc spring : springs) {
2655                if (spring.getAlignment() == null ||
2656                        spring.getAlignment() == Alignment.BASELINE) {
2657                    int baseline = spring.getBaseline();
2658                    if (baseline >= 0) {
2659                        if (spring.isResizable(VERTICAL)) {
2660                            BaselineResizeBehavior brb = spring.
2661                                    getBaselineResizeBehavior();
2662                            if (resizeBehavior == null) {
2663                                resizeBehavior = brb;
2664                            } else if (brb != resizeBehavior) {
2665                                resizeBehavior = BaselineResizeBehavior.
2666                                        CONSTANT_ASCENT;
2667                            }
2668                        }
2669                        prefAscent = Math.max(prefAscent, baseline);
2670                        prefDescent = Math.max(prefDescent, spring.
2671                                getPreferredSize(VERTICAL) - baseline);
2672                        baselineSpringCount++;
2673                    }
2674                }
2675            }
2676            if (!baselineAnchorSet) {
2677                if (resizeBehavior == BaselineResizeBehavior.CONSTANT_DESCENT){
2678                    this.baselineAnchoredToTop = false;
2679                } else {
2680                    this.baselineAnchoredToTop = true;
2681                }
2682            }
2683            allSpringsHaveBaseline = (baselineSpringCount == springs.size());
2684            calcedBaseline = true;
2685        }
2686        
2687        private int calculateMaxSize() {
2688            int maxAscent = prefAscent;
2689            int maxDescent = prefDescent;
2690            int nonBaselineMax = 0;
2691            for (Spring JavaDoc spring : springs) {
2692                int baseline;
2693                int springMax = spring.getMaximumSize(VERTICAL);
2694                if ((spring.getAlignment() == null ||
2695                        spring.getAlignment() == Alignment.BASELINE) &&
2696                        (baseline = spring.getBaseline()) >= 0) {
2697                    int springPref = spring.getPreferredSize(VERTICAL);
2698                    if (springPref != springMax) {
2699                        switch (spring.getBaselineResizeBehavior()) {
2700                            case CONSTANT_ASCENT:
2701                                if (baselineAnchoredToTop) {
2702                                    maxDescent = Math.max(maxDescent,
2703                                            springMax - baseline);
2704                                }
2705                                break;
2706                            case CONSTANT_DESCENT:
2707                                if (!baselineAnchoredToTop) {
2708                                    maxAscent = Math.max(maxAscent,
2709                                            springMax - springPref + baseline);
2710                                }
2711                                break;
2712                            default: // CENTER_OFFSET and OTHER, not resizable
2713
break;
2714                        }
2715                    }
2716                } else {
2717                    // Not aligned along the baseline, or no baseline.
2718
nonBaselineMax = Math.max(nonBaselineMax, springMax);
2719                }
2720            }
2721            return Math.max(nonBaselineMax, maxAscent + maxDescent);
2722        }
2723        
2724        private int calculateMinSize() {
2725            int minAscent = 0;
2726            int minDescent = 0;
2727            int nonBaselineMin = 0;
2728            if (baselineAnchoredToTop) {
2729                minAscent = prefAscent;
2730            } else {
2731                minDescent = prefDescent;
2732            }
2733            for (Spring JavaDoc spring : springs) {
2734                int springMin = spring.getMinimumSize(VERTICAL);
2735                int baseline;
2736                if ((spring.getAlignment() == null ||
2737                        spring.getAlignment() == Alignment.BASELINE) &&
2738                        (baseline = spring.getBaseline()) >= 0) {
2739                    int springPref = spring.getPreferredSize(VERTICAL);
2740                    BaselineResizeBehavior brb = spring.
2741                            getBaselineResizeBehavior();
2742                    switch (brb) {
2743                        case CONSTANT_ASCENT:
2744                            if (baselineAnchoredToTop) {
2745                                minDescent = Math.max(springMin - baseline,
2746                                        minDescent);
2747                            } else {
2748                                minAscent = Math.max(baseline, minAscent);
2749                            }
2750                            break;
2751                        case CONSTANT_DESCENT:
2752                            if (!baselineAnchoredToTop) {
2753                                minAscent = Math.max(
2754                                        baseline - (springPref - springMin),
2755                                        minAscent);
2756                            } else {
2757                                minDescent = Math.max(springPref - baseline,
2758                                        minDescent);
2759                            }
2760                            break;
2761                        default:
2762                            // CENTER_OFFSET and OTHER are !resizable, use
2763
// the preferred size.
2764
minAscent = Math.max(baseline, minAscent);
2765                            minDescent = Math.max(springPref - baseline,
2766                                    minDescent);
2767                            break;
2768                    }
2769                } else {
2770                    // Not aligned along the baseline, or no baseline.
2771
nonBaselineMin = Math.max(nonBaselineMin, springMin);
2772                }
2773            }
2774            return Math.max(nonBaselineMin, minAscent + minDescent);
2775        }
2776        
2777        /**
2778         * Lays out springs that have a baseline along the baseline. All
2779         * others are centered.
2780         */

2781        private void baselineLayout(int origin, int size) {
2782            int ascent;
2783            int descent;
2784            if (baselineAnchoredToTop) {
2785                ascent = prefAscent;
2786                descent = size - ascent;
2787            } else {
2788                ascent = size - prefDescent;
2789                descent = prefDescent;
2790            }
2791            for (Spring JavaDoc spring : springs) {
2792                Alignment alignment = spring.getAlignment();
2793                if (alignment == null || alignment == Alignment.BASELINE) {
2794                    int baseline = spring.getBaseline();
2795                    if (baseline >= 0) {
2796                        int springMax = spring.getMaximumSize(VERTICAL);
2797                        int springPref = spring.getPreferredSize(VERTICAL);
2798                        int height = springPref;
2799                        int y;
2800                        switch(spring.getBaselineResizeBehavior()) {
2801                            case CONSTANT_ASCENT:
2802                                y = origin + ascent - baseline;
2803                                height = Math.min(descent, springMax -
2804                                        baseline) + baseline;
2805                                break;
2806                            case CONSTANT_DESCENT:
2807                                height = Math.min(ascent, springMax -
2808                                        springPref + baseline) +
2809                                        (springPref - baseline);
2810                                y = origin + ascent +
2811                                        (springPref - baseline) - height;
2812                                break;
2813                            default: // CENTER_OFFSET & OTHER, not resizable
2814
y = origin + ascent - baseline;
2815                                break;
2816                        }
2817                        spring.setSize(VERTICAL, y, height);
2818                    } else {
2819                        setChildSize(spring, VERTICAL, origin, size);
2820                    }
2821                } else {
2822                    setChildSize(spring, VERTICAL, origin, size);
2823                }
2824            }
2825        }
2826        
2827        int getBaseline() {
2828            if (springs.size() > 1) {
2829                // Force the baseline to be calculated
2830
getPreferredSize(VERTICAL);
2831                return prefAscent;
2832            } else if (springs.size() == 1) {
2833                return springs.get(0).getBaseline();
2834            }
2835            return -1;
2836        }
2837        
2838        BaselineResizeBehavior getBaselineResizeBehavior() {
2839            if (springs.size() == 1) {
2840                return springs.get(0).getBaselineResizeBehavior();
2841            }
2842            if (baselineAnchoredToTop) {
2843                return BaselineResizeBehavior.CONSTANT_ASCENT;
2844            }
2845            return BaselineResizeBehavior.CONSTANT_DESCENT;
2846        }
2847        
2848        // If the axis is VERTICAL, throws an IllegalStateException
2849
private void checkAxis(int axis) {
2850            if (axis == HORIZONTAL) {
2851                throw new IllegalStateException JavaDoc(
2852                        "Baseline must be used along vertical axis");
2853            }
2854        }
2855    }
2856
2857
2858    private final class ComponentSpring extends Spring JavaDoc {
2859        private Component JavaDoc component;
2860        private int origin;
2861        
2862        // min/pref/max are either a value >= 0 or one of
2863
// DEFAULT_SIZE or PREFERRED_SIZE
2864
private final int min;
2865        private final int pref;
2866        private final int max;
2867        
2868        // Baseline for the component, computed as necessary.
2869
private int baseline = -1;
2870        
2871        // Whether or not the size has been requested yet.
2872
private boolean installed;
2873        
2874        private ComponentSpring(Component JavaDoc component, int min, int pref,
2875                int max) {
2876            this.component = component;
2877            if (component == null) {
2878                throw new IllegalArgumentException JavaDoc(
2879                        "Component must be non-null");
2880            }
2881            
2882            checkSize(min, pref, max, true);
2883            
2884            this.min = min;
2885            this.max = max;
2886            this.pref = pref;
2887            
2888            // getComponentInfo makes sure component is a child of the
2889
// Container GroupLayout is the LayoutManager for.
2890
getComponentInfo(component);
2891        }
2892        
2893        int calculateMinimumSize(int axis) {
2894            if (isLinked(axis)) {
2895                return getLinkSize(axis, MIN_SIZE);
2896            }
2897            return calculateNonlinkedMinimumSize(axis);
2898        }
2899        
2900        int calculatePreferredSize(int axis) {
2901            if (isLinked(axis)) {
2902                return getLinkSize(axis, PREF_SIZE);
2903            }
2904            int min = getMinimumSize(axis);
2905            int pref = calculateNonlinkedPreferredSize(axis);
2906            int max = getMaximumSize(axis);
2907            return Math.min(max, Math.max(min, pref));
2908        }
2909        
2910        int calculateMaximumSize(int axis) {
2911            if (isLinked(axis)) {
2912                return getLinkSize(axis, MAX_SIZE);
2913            }
2914            return Math.max(getMinimumSize(axis),
2915                    calculateNonlinkedMaximumSize(axis));
2916        }
2917        
2918        boolean isVisible() {
2919            return getComponentInfo(getComponent()).isVisible();
2920        }
2921        
2922        int calculateNonlinkedMinimumSize(int axis) {
2923            if (!isVisible()) {
2924                return 0;
2925            }
2926            if (min >= 0) {
2927                return min;
2928            }
2929            if (min == PREFERRED_SIZE) {
2930                return calculateNonlinkedPreferredSize(axis);
2931            }
2932            assert (min == DEFAULT_SIZE);
2933            return getSizeAlongAxis(axis, component.getMinimumSize());
2934        }
2935        
2936        int calculateNonlinkedPreferredSize(int axis) {
2937            if (!isVisible()) {
2938                return 0;
2939            }
2940            if (pref >= 0) {
2941                return pref;
2942            }
2943            assert (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE);
2944            return getSizeAlongAxis(axis, component.getPreferredSize());
2945        }
2946        
2947        int calculateNonlinkedMaximumSize(int axis) {
2948            if (!isVisible()) {
2949                return 0;
2950            }
2951            if (max >= 0) {
2952                return max;
2953            }
2954            if (max == PREFERRED_SIZE) {
2955                return calculateNonlinkedPreferredSize(axis);
2956            }
2957            assert (max == DEFAULT_SIZE);
2958            return getSizeAlongAxis(axis, component.getMaximumSize());
2959        }
2960        
2961        private int getSizeAlongAxis(int axis, Dimension JavaDoc size) {
2962            return (axis == HORIZONTAL) ? size.width : size.height;
2963        }
2964        
2965        private int getLinkSize(int axis, int type) {
2966            if (!isVisible()) {
2967                return 0;
2968            }
2969            ComponentInfo ci = getComponentInfo(component);
2970            return ci.getLinkSize(axis, type);
2971        }
2972        
2973        void setSize(int axis, int origin, int size) {
2974            super.setSize(axis, origin, size);
2975            this.origin = origin;
2976            if (size == UNSET) {
2977                baseline = -1;
2978            }
2979        }
2980        
2981        int getOrigin() {
2982            return origin;
2983        }
2984        
2985        void setComponent(Component JavaDoc component) {
2986            this.component = component;
2987        }
2988        
2989        Component JavaDoc getComponent() {
2990            return component;
2991        }
2992        
2993        int getBaseline() {
2994            if (baseline == -1) {
2995                Spring JavaDoc horizontalSpring = getComponentInfo(component).
2996                        horizontalSpring;
2997                int width = horizontalSpring.getPreferredSize(HORIZONTAL);
2998                int height = getPreferredSize(VERTICAL);
2999                if (width > 0 && height > 0) {
3000                    baseline = component.getBaseline(width, height);
3001                }
3002            }
3003            return baseline;
3004        }
3005
3006        BaselineResizeBehavior getBaselineResizeBehavior() {
3007            return getComponent().getBaselineResizeBehavior();
3008        }
3009
3010        private boolean isLinked(int axis) {
3011            return getComponentInfo(component).isLinked(axis);
3012        }
3013        
3014        void installIfNecessary(int axis) {
3015            if (!installed) {
3016                installed = true;
3017                if (axis == HORIZONTAL) {
3018                    getComponentInfo(component).horizontalSpring = this;
3019                } else {
3020                    getComponentInfo(component).verticalSpring = this;
3021                }
3022            }
3023        }
3024    }
3025    
3026    
3027    /**
3028     * Spring representing the preferred distance between two components.
3029     */

3030    private class PreferredGapSpring extends Spring JavaDoc {
3031        private final JComponent JavaDoc source;
3032        private final JComponent JavaDoc target;
3033        private final ComponentPlacement type;
3034        private final int pref;
3035        private final int max;
3036        
3037        PreferredGapSpring(JComponent JavaDoc source, JComponent JavaDoc target,
3038                ComponentPlacement type, int pref, int max) {
3039            this.source = source;
3040            this.target = target;
3041            this.type = type;
3042            this.pref = pref;
3043            this.max = max;
3044        }
3045        
3046        int calculateMinimumSize(int axis) {
3047            return getPadding(axis);
3048        }
3049        
3050        int calculatePreferredSize(int axis) {
3051            if (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE) {
3052                return getMinimumSize(axis);
3053            }
3054            int min = getMinimumSize(axis);
3055            int max = getMaximumSize(axis);
3056            return Math.min(max, Math.max(min, pref));
3057        }
3058        
3059        int calculateMaximumSize(int axis) {
3060            if (max == PREFERRED_SIZE || max == DEFAULT_SIZE) {
3061                return getPadding(axis);
3062            }
3063            return Math.max(getMinimumSize(axis), max);
3064        }
3065        
3066        private int getPadding(int axis) {
3067            int position;
3068            if (axis == HORIZONTAL) {
3069                position = SwingConstants.EAST;
3070            } else {
3071                position = SwingConstants.SOUTH;
3072            }
3073            return getLayoutStyle0().getPreferredGap(source,
3074                    target, type, position, host);
3075        }
3076    }
3077    
3078    
3079    /**
3080     * Spring represented a certain amount of space.
3081     */

3082    private class GapSpring extends Spring JavaDoc {
3083        private final int min;
3084        private final int pref;
3085        private final int max;
3086        
3087        GapSpring(int min, int pref, int max) {
3088            checkSize(min, pref, max, false);
3089            this.min = min;
3090            this.pref = pref;
3091            this.max = max;
3092        }
3093        
3094        int calculateMinimumSize(int axis) {
3095            if (min == PREFERRED_SIZE) {
3096                return getPreferredSize(axis);
3097            }
3098            return min;
3099        }
3100        
3101        int calculatePreferredSize(int axis) {
3102            return pref;
3103        }
3104        
3105        int calculateMaximumSize(int axis) {
3106            if (max == PREFERRED_SIZE) {
3107                return getPreferredSize(axis);
3108            }
3109            return max;
3110        }
3111    }
3112    
3113    
3114    /**
3115     * Spring reprensenting the distance between any number of sources and
3116     * targets. The targets and sources are computed during layout. An
3117     * instance of this can either be dynamically created when
3118     * autocreatePadding is true, or explicitly created by the developer.
3119     */

3120    private class AutoPreferredGapSpring extends Spring JavaDoc {
3121        List<ComponentSpring> sources;
3122        ComponentSpring source;
3123        private List<AutoPreferredGapMatch> matches;
3124        int size;
3125        int lastSize;
3126        private final int pref;
3127        private final int max;
3128        // Type of gap
3129
private ComponentPlacement type;
3130        private boolean userCreated;
3131        
3132        private AutoPreferredGapSpring() {
3133            this.pref = PREFERRED_SIZE;
3134            this.max = PREFERRED_SIZE;
3135            this.type = ComponentPlacement.RELATED;
3136        }
3137        
3138        AutoPreferredGapSpring(int pref, int max) {
3139            this.pref = pref;
3140            this.max = max;
3141        }
3142        
3143        AutoPreferredGapSpring(ComponentPlacement type, int pref, int max) {
3144            this.type = type;
3145            this.pref = pref;
3146            this.max = max;
3147            this.userCreated = true;
3148        }
3149        
3150        public void setSource(ComponentSpring source) {
3151            this.source = source;
3152        }
3153        
3154        public void setSources(List<ComponentSpring> sources) {
3155            this.sources = new ArrayList<ComponentSpring>(sources);
3156        }
3157        
3158        public void setUserCreated(boolean userCreated) {
3159            this.userCreated = userCreated;
3160        }
3161        
3162        public boolean getUserCreated() {
3163            return userCreated;
3164        }
3165        
3166        void unset() {
3167            lastSize = getSize();
3168            super.unset();
3169            size = 0;
3170        }
3171        
3172        public void reset() {
3173            size = 0;
3174            sources = null;
3175            source = null;
3176            matches = null;
3177        }
3178        
3179        public void calculatePadding(int axis) {
3180            size = UNSET;
3181            int maxPadding = UNSET;
3182            if (matches != null) {
3183                LayoutStyle JavaDoc p = getLayoutStyle0();
3184                int position;
3185                if (axis == HORIZONTAL) {
3186                    if (isLeftToRight()) {
3187                        position = SwingConstants.EAST;
3188                    } else {
3189                        position = SwingConstants.WEST;
3190                    }
3191                } else {
3192                    position = SwingConstants.SOUTH;
3193                }
3194                for (int i = matches.size() - 1; i >= 0; i--) {
3195                    AutoPreferredGapMatch match = matches.get(i);
3196                    maxPadding = Math.max(maxPadding,
3197                            calculatePadding(p, position, match.source,
3198                            match.target));
3199                }
3200            }
3201            if (size == UNSET) {
3202                size = 0;
3203            }
3204            if (maxPadding == UNSET) {
3205                maxPadding = 0;
3206            }
3207            if (lastSize != UNSET) {
3208                size += Math.min(maxPadding, lastSize);
3209            }
3210        }
3211        
3212        private int calculatePadding(LayoutStyle JavaDoc p, int position,
3213                ComponentSpring source,
3214                ComponentSpring target) {
3215            int delta = target.getOrigin() - (source.getOrigin() +
3216                    source.getSize());
3217            if (delta >= 0) {
3218                int padding;
3219                if ((source.getComponent() instanceof JComponent JavaDoc) &&
3220                        (target.getComponent() instanceof JComponent JavaDoc)) {
3221                    padding = p.getPreferredGap(
3222                            (JComponent JavaDoc)source.getComponent(),
3223                            (JComponent JavaDoc)target.getComponent(), type, position,
3224                            host);
3225                } else {
3226                    padding = 10;
3227                }
3228                if (padding > delta) {
3229                    size = Math.max(size, padding - delta);
3230                }
3231                return padding;
3232            }
3233            return 0;
3234        }
3235        
3236        public void addTarget(ComponentSpring spring, int axis) {
3237            int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL;
3238            if (source != null) {
3239                if (areParallelSiblings(source.getComponent(),
3240                        spring.getComponent(), oAxis)) {
3241                    addValidTarget(source, spring);
3242                }
3243            } else {
3244                Component JavaDoc component = spring.getComponent();
3245                for (int counter = sources.size() - 1; counter >= 0;
3246                         counter--){
3247                    ComponentSpring source = sources.get(counter);
3248                    if (areParallelSiblings(source.getComponent(),
3249                            component, oAxis)) {
3250                        addValidTarget(source, spring);
3251                    }
3252                }
3253            }
3254        }
3255        
3256        private void addValidTarget(ComponentSpring source,
3257                ComponentSpring target) {
3258            if (matches == null) {
3259                matches = new ArrayList<AutoPreferredGapMatch>(1);
3260            }
3261            matches.add(new AutoPreferredGapMatch(source, target));
3262        }
3263        
3264        int calculateMinimumSize(int axis) {
3265            return size;
3266        }
3267        
3268        int calculatePreferredSize(int axis) {
3269            if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) {
3270                return size;
3271            }
3272            return Math.max(size, pref);
3273        }
3274        
3275        int calculateMaximumSize(int axis) {
3276            if (max >= 0) {
3277                return Math.max(getPreferredSize(axis), max);
3278            }
3279            return size;
3280        }
3281        
3282        String JavaDoc getMatchDescription() {
3283            return (matches == null) ? "" : matches.toString();
3284        }
3285        
3286        public String JavaDoc toString() {
3287            return super.toString() + getMatchDescription();
3288        }
3289    }
3290    
3291    
3292    /**
3293     * Represents two springs that should have autopadding inserted between
3294     * them.
3295     */

3296    private final static class AutoPreferredGapMatch {
3297        public final ComponentSpring source;
3298        public final ComponentSpring target;
3299        
3300        AutoPreferredGapMatch(ComponentSpring source, ComponentSpring target) {
3301            this.source = source;
3302            this.target = target;
3303        }
3304        
3305        private String JavaDoc toString(ComponentSpring spring) {
3306            return spring.getComponent().getName();
3307        }
3308        
3309        public String JavaDoc toString() {
3310            return "[" + toString(source) + "-" + toString(target) + "]";
3311        }
3312    }
3313    
3314    
3315    /**
3316     * An extension of AutopaddingSpring used for container level padding.
3317     */

3318    private class ContainerAutoPreferredGapSpring extends
3319            AutoPreferredGapSpring {
3320        private List<ComponentSpring> targets;
3321        
3322        ContainerAutoPreferredGapSpring() {
3323            super();
3324            setUserCreated(true);
3325        }
3326        
3327        ContainerAutoPreferredGapSpring(int pref, int max) {
3328            super(pref, max);
3329            setUserCreated(true);
3330        }
3331        
3332        public void addTarget(ComponentSpring spring, int axis) {
3333            if (targets == null) {
3334                targets = new ArrayList<ComponentSpring>(1);
3335            }
3336            targets.add(spring);
3337        }
3338        
3339        public void calculatePadding(int axis) {
3340            LayoutStyle JavaDoc p = getLayoutStyle0();
3341            int maxPadding = 0;
3342            int position;
3343            size = 0;
3344            if (targets != null) {
3345                // Leading
3346
if (axis == HORIZONTAL) {
3347                    if (isLeftToRight()) {
3348                        position = SwingConstants.WEST;
3349                    } else {
3350                        position = SwingConstants.EAST;
3351                    }
3352                } else {
3353                    position = SwingConstants.SOUTH;
3354                }
3355                for (int i = targets.size() - 1; i >= 0; i--) {
3356                    ComponentSpring targetSpring = targets.get(i);
3357                    int padding = 10;
3358                    if (targetSpring.getComponent() instanceof JComponent JavaDoc) {
3359                        padding = p.getContainerGap(
3360                                (JComponent JavaDoc)targetSpring.getComponent(),
3361                                position, host);
3362                        maxPadding = Math.max(padding, maxPadding);
3363                        padding -= targetSpring.getOrigin();
3364                    } else {
3365                        maxPadding = Math.max(padding, maxPadding);
3366                    }
3367                    size = Math.max(size, padding);
3368                }
3369            } else {
3370                // Trailing
3371
if (axis == HORIZONTAL) {
3372                    if (isLeftToRight()) {
3373                        position = SwingConstants.EAST;
3374                    } else {
3375                        position = SwingConstants.WEST;
3376                    }
3377                } else {
3378                    position = SwingConstants.SOUTH;
3379                }
3380                if (sources != null) {
3381                    for (int i = sources.size() - 1; i >= 0; i--) {
3382                        ComponentSpring sourceSpring = sources.get(i);
3383                        maxPadding = Math.max(maxPadding,
3384                                updateSize(p, sourceSpring, position));
3385                    }
3386                } else if (source != null) {
3387                    maxPadding = updateSize(p, source, position);
3388                }
3389            }
3390            if (lastSize != UNSET) {
3391                size += Math.min(maxPadding, lastSize);
3392            }
3393        }
3394        
3395        private int updateSize(LayoutStyle JavaDoc p, ComponentSpring sourceSpring,
3396                int position) {
3397            int padding = 10;
3398            if (sourceSpring.getComponent() instanceof JComponent JavaDoc) {
3399                padding = p.getContainerGap(
3400                        (JComponent JavaDoc)sourceSpring.getComponent(), position,
3401                        host);
3402            }
3403            int delta = Math.max(0, getParent().getSize() -
3404                    sourceSpring.getSize() - sourceSpring.getOrigin());
3405            size = Math.max(size, padding - delta);
3406            return padding;
3407        }
3408        
3409        String JavaDoc getMatchDescription() {
3410            if (targets != null) {
3411                return "leading: " + targets.toString();
3412            }
3413            if (sources != null) {
3414                return "trailing: " + sources.toString();
3415            }
3416            return "--";
3417        }
3418    }
3419    
3420    
3421    // LinkInfo contains the set of ComponentInfosthat are linked along a
3422
// particular axis.
3423
private static class LinkInfo {
3424        private final int axis;
3425        private final List<ComponentInfo> linked;
3426        private int size;
3427        
3428        LinkInfo(int axis) {
3429            linked = new ArrayList<ComponentInfo>();
3430            size = UNSET;
3431            this.axis = axis;
3432        }
3433        
3434        public void add(ComponentInfo child) {
3435            LinkInfo childMaster = child.getLinkInfo(axis, false);
3436            if (childMaster == null) {
3437                linked.add(child);
3438                child.setLinkInfo(axis, this);
3439            } else if (childMaster != this) {
3440                linked.addAll(childMaster.linked);
3441                for (ComponentInfo childInfo : childMaster.linked) {
3442                    childInfo.setLinkInfo(axis, this);
3443                }
3444            }
3445            clearCachedSize();
3446        }
3447        
3448        public void remove(ComponentInfo info) {
3449            linked.remove(info);
3450            info.setLinkInfo(axis, null);
3451            if (linked.size() == 1) {
3452                linked.get(0).setLinkInfo(axis, null);
3453            }
3454            clearCachedSize();
3455        }
3456        
3457        public void clearCachedSize() {
3458            size = UNSET;
3459        }
3460        
3461        public int getSize(int axis) {
3462            if (size == UNSET) {
3463                size = calculateLinkedSize(axis);
3464            }
3465            return size;
3466        }
3467        
3468        private int calculateLinkedSize(int axis) {
3469            int size = 0;
3470            for (ComponentInfo info : linked) {
3471                ComponentSpring spring;
3472                if (axis == HORIZONTAL) {
3473                    spring = info.horizontalSpring;
3474                } else {
3475                    assert (axis == VERTICAL);
3476                    spring = info.verticalSpring;
3477                }
3478                size = Math.max(size,
3479                        spring.calculateNonlinkedPreferredSize(axis));
3480            }
3481            return size;
3482        }
3483    }
3484    
3485    /**
3486     * Tracks the horizontal/vertical Springs for a Component.
3487     * This class is also used to handle Springs that have their sizes
3488     * linked.
3489     */

3490    private class ComponentInfo {
3491        // Component being layed out
3492
private Component JavaDoc component;
3493        
3494        ComponentSpring horizontalSpring;
3495        ComponentSpring verticalSpring;
3496        
3497        // If the component's size is linked to other components, the
3498
// horizontalMaster and/or verticalMaster reference the group of
3499
// linked components.
3500
private LinkInfo horizontalMaster;
3501        private LinkInfo verticalMaster;
3502
3503        private boolean visible;
3504        private Boolean JavaDoc honorsVisibility;
3505        
3506        ComponentInfo(Component JavaDoc component) {
3507            this.component = component;
3508            updateVisibility();
3509        }
3510        
3511        public void dispose() {
3512            // Remove horizontal/vertical springs
3513
removeSpring(horizontalSpring);
3514            horizontalSpring = null;
3515            removeSpring(verticalSpring);
3516            verticalSpring = null;
3517            // Clean up links
3518
if (horizontalMaster != null) {
3519                horizontalMaster.remove(this);
3520            }
3521            if (verticalMaster != null) {
3522                verticalMaster.remove(this);
3523            }
3524        }
3525        
3526        void setHonorsVisibility(Boolean JavaDoc honorsVisibility) {
3527            this.honorsVisibility = honorsVisibility;
3528        }
3529
3530        private void removeSpring(Spring JavaDoc spring) {
3531            if (spring != null) {
3532                ((Group)spring.getParent()).springs.remove(spring);
3533            }
3534        }
3535        
3536        public boolean isVisible() {
3537            return visible;
3538        }
3539        
3540        /**
3541         * Updates the cached visibility.
3542         *
3543         * @return true if the visibility changed
3544         */

3545        boolean updateVisibility() {
3546            boolean honorsVisibility;
3547            if (this.honorsVisibility == null) {
3548                honorsVisibility = GroupLayout.this.getHonorsVisibility();
3549            } else {
3550                honorsVisibility = this.honorsVisibility;
3551            }
3552            boolean newVisible = (honorsVisibility) ?
3553                component.isVisible() : true;
3554            if (visible != newVisible) {
3555                visible = newVisible;
3556                return true;
3557            }
3558            return false;
3559        }
3560        
3561        public void setBounds(Insets JavaDoc insets, int parentWidth, boolean ltr) {
3562            int x = horizontalSpring.getOrigin();
3563            int w = horizontalSpring.getSize();
3564            int y = verticalSpring.getOrigin();
3565            int h = verticalSpring.getSize();
3566            
3567            if (!ltr) {
3568                x = parentWidth - x - w;
3569            }
3570            component.setBounds(x + insets.left, y + insets.top, w, h);
3571        }
3572        
3573        public void setComponent(Component JavaDoc component) {
3574            this.component = component;
3575            if (horizontalSpring != null) {
3576                horizontalSpring.setComponent(component);
3577            }
3578            if (verticalSpring != null) {
3579                verticalSpring.setComponent(component);
3580            }
3581        }
3582        
3583        public Component JavaDoc getComponent() {
3584            return component;
3585        }
3586        
3587        /**
3588         * Returns true if this component has its size linked to
3589         * other components.
3590         */

3591        public boolean isLinked(int axis) {
3592            if (axis == HORIZONTAL) {
3593                return horizontalMaster != null;
3594            }
3595            assert (axis == VERTICAL);
3596            return (verticalMaster != null);
3597        }
3598        
3599        private void setLinkInfo(int axis, LinkInfo linkInfo) {
3600            if (axis == HORIZONTAL) {
3601                horizontalMaster = linkInfo;
3602            } else {
3603                assert (axis == VERTICAL);
3604                verticalMaster = linkInfo;
3605            }
3606        }
3607        
3608        public LinkInfo getLinkInfo(int axis) {
3609            return getLinkInfo(axis, true);
3610        }
3611        
3612        private LinkInfo getLinkInfo(int axis, boolean create) {
3613            if (axis == HORIZONTAL) {
3614                if (horizontalMaster == null && create) {
3615                    // horizontalMaster field is directly set by adding
3616
// us to the LinkInfo.
3617
new LinkInfo(HORIZONTAL).add(this);
3618                }
3619                return horizontalMaster;
3620            } else {
3621                assert (axis == VERTICAL);
3622                if (verticalMaster == null && create) {
3623                    // verticalMaster field is directly set by adding
3624
// us to the LinkInfo.
3625
new LinkInfo(VERTICAL).add(this);
3626                }
3627                return verticalMaster;
3628            }
3629        }
3630
3631        public void clearCachedSize() {
3632            if (horizontalMaster != null) {
3633                horizontalMaster.clearCachedSize();
3634            }
3635            if (verticalMaster != null) {
3636                verticalMaster.clearCachedSize();
3637            }
3638        }
3639        
3640        int getLinkSize(int axis, int type) {
3641            if (axis == HORIZONTAL) {
3642                return horizontalMaster.getSize(axis);
3643            } else {
3644                assert (axis == VERTICAL);
3645                return verticalMaster.getSize(axis);
3646            }
3647        }
3648        
3649    }
3650}
3651
Popular Tags