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     &nb