History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

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

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

Unified · Split

java/pkg/javax/swing/SpringLayout.java+1233−0
@@ -0,0 +1,1233 @@
1+/*
2+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+package javax.swing;
26+
27+import java.awt.Component;
28+import java.awt.Container;
29+import java.awt.Dimension;
30+import java.awt.FontMetrics;
31+import java.awt.Insets;
32+import java.awt.LayoutManager2;
33+import java.awt.Rectangle;
34+import java.util.*;
35+
36+/**
37+ * A <code>SpringLayout</code> lays out the children of its associated container
38+ * according to a set of constraints.
39+ * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a>
40+ * in <em>The Java Tutorial</em> for examples of using
41+ * <code>SpringLayout</code>.
42+ *
43+ * <p>
44+ * Each constraint,
45+ * represented by a <code>Spring</code> object,
46+ * controls the vertical or horizontal distance
47+ * between two component edges.
48+ * The edges can belong to
49+ * any child of the container,
50+ * or to the container itself.
51+ * For example,
52+ * the allowable width of a component
53+ * can be expressed using a constraint
54+ * that controls the distance between the west (left) and east (right)
55+ * edges of the component.
56+ * The allowable <em>y</em> coordinates for a component
57+ * can be expressed by constraining the distance between
58+ * the north (top) edge of the component
59+ * and the north edge of its container.
60+ *
61+ * <P>
62+ * Every child of a <code>SpringLayout</code>-controlled container,
63+ * as well as the container itself,
64+ * has exactly one set of constraints
65+ * associated with it.
66+ * These constraints are represented by
67+ * a <code>SpringLayout.Constraints</code> object.
68+ * By default,
69+ * <code>SpringLayout</code> creates constraints
70+ * that make their associated component
71+ * have the minimum, preferred, and maximum sizes
72+ * returned by the component's
73+ * {@link java.awt.Component#getMinimumSize},
74+ * {@link java.awt.Component#getPreferredSize}, and
75+ * {@link java.awt.Component#getMaximumSize}
76+ * methods. The <em>x</em> and <em>y</em> positions are initially not
77+ * constrained, so that until you constrain them the <code>Component</code>
78+ * will be positioned at 0,0 relative to the <code>Insets</code> of the
79+ * parent <code>Container</code>.
80+ *
81+ * <p>
82+ * You can change
83+ * a component's constraints in several ways.
84+ * You can
85+ * use one of the
86+ * {@link #putConstraint putConstraint}
87+ * methods
88+ * to establish a spring
89+ * linking the edges of two components within the same container.
90+ * Or you can get the appropriate <code>SpringLayout.Constraints</code>
91+ * object using
92+ * {@link #getConstraints getConstraints}
93+ * and then modify one or more of its springs.
94+ * Or you can get the spring for a particular edge of a component
95+ * using {@link #getConstraint getConstraint},
96+ * and modify it.
97+ * You can also associate
98+ * your own <code>SpringLayout.Constraints</code> object
99+ * with a component by specifying the constraints object
100+ * when you add the component to its container
101+ * (using
102+ * {@link Container#add(Component, Object)}).
103+ *
104+ * <p>
105+ * The <code>Spring</code> object representing each constraint
106+ * has a minimum, preferred, maximum, and current value.
107+ * The current value of the spring
108+ * is somewhere between the minimum and maximum values,
109+ * according to the formula given in the
110+ * {@link Spring#sum} method description.
111+ * When the minimum, preferred, and maximum values are the same,
112+ * the current value is always equal to them;
113+ * this inflexible spring is called a <em>strut</em>.
114+ * You can create struts using the factory method
115+ * {@link Spring#constant(int)}.
116+ * The <code>Spring</code> class also provides factory methods
117+ * for creating other kinds of springs,
118+ * including springs that depend on other springs.
119+ *
120+ * <p>
121+ * In a <code>SpringLayout</code>, the position of each edge is dependent on
122+ * the position of just one other edge. If a constraint is subsequently added
123+ * to create a new binding for an edge, the previous binding is discarded
124+ * and the edge remains dependent on a single edge.
125+ * Springs should only be attached
126+ * between edges of the container and its immediate children; the behavior
127+ * of the <code>SpringLayout</code> when presented with constraints linking
128+ * the edges of components from different containers (either internal or
129+ * external) is undefined.
130+ *
131+ * <h3>
132+ * SpringLayout vs. Other Layout Managers
133+ * </h3>
134+ *
135+ * <blockquote>
136+ * <hr>
137+ * <strong>Note:</strong>
138+ * Unlike many layout managers,
139+ * <code>SpringLayout</code> doesn't automatically set the location of
140+ * the components it manages.
141+ * If you hand-code a GUI that uses <code>SpringLayout</code>,
142+ * remember to initialize component locations by constraining the west/east
143+ * and north/south locations.
144+ * <p>
145+ * Depending on the constraints you use,
146+ * you may also need to set the size of the container explicitly.
147+ * <hr>
148+ * </blockquote>
149+ *
150+ * <p>
151+ * Despite the simplicity of <code>SpringLayout</code>,
152+ * it can emulate the behavior of most other layout managers.
153+ * For some features,
154+ * such as the line breaking provided by <code>FlowLayout</code>,
155+ * you'll need to
156+ * create a special-purpose subclass of the <code>Spring</code> class.
157+ *
158+ * <p>
159+ * <code>SpringLayout</code> also provides a way to solve
160+ * many of the difficult layout
161+ * problems that cannot be solved by nesting combinations
162+ * of <code>Box</code>es. That said, <code>SpringLayout</code> honors the
163+ * <code>LayoutManager2</code> contract correctly and so can be nested with
164+ * other layout managers -- a technique that can be preferable to
165+ * creating the constraints implied by the other layout managers.
166+ * <p>
167+ * The asymptotic complexity of the layout operation of a <code>SpringLayout</code>
168+ * is linear in the number of constraints (and/or components).
169+ * <p>
170+ * <strong>Warning:</strong>
171+ * Serialized objects of this class will not be compatible with
172+ * future Swing releases. The current serialization support is
173+ * appropriate for short term storage or RMI between applications running
174+ * the same version of Swing. As of 1.4, support for long term storage
175+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
176+ * has been added to the <code>java.beans</code> package.
177+ * Please see {@link java.beans.XMLEncoder}.
178+ *
179+ * @see Spring
180+ * @see SpringLayout.Constraints
181+ *
182+ * @author Philip Milne
183+ * @author Scott Violet
184+ * @author Joe Winchester
185+ * @since 1.4
186+ */
187+public class SpringLayout implements LayoutManager2 {
188+ private Map<Component, Constraints> componentConstraints = new HashMap<Component, Constraints>();
189+
190+ private Spring cyclicReference = Spring.constant(Spring.UNSET);
191+ private Set<Spring> cyclicSprings;
192+ private Set<Spring> acyclicSprings;
193+
194+
195+ /**
196+ * Specifies the top edge of a component's bounding rectangle.
197+ */
198+ public static final String NORTH = "North";
199+
200+ /**
201+ * Specifies the bottom edge of a component's bounding rectangle.
202+ */
203+ public static final String SOUTH = "South";
204+
205+ /**
206+ * Specifies the right edge of a component's bounding rectangle.
207+ */
208+ public static final String EAST = "East";
209+
210+ /**
211+ * Specifies the left edge of a component's bounding rectangle.
212+ */
213+ public static final String WEST = "West";
214+
215+ /**
216+ * Specifies the horizontal center of a component's bounding rectangle.
217+ *
218+ * @since 1.6
219+ */
220+ public static final String HORIZONTAL_CENTER = "HorizontalCenter";
221+
222+ /**
223+ * Specifies the vertical center of a component's bounding rectangle.
224+ *
225+ * @since 1.6
226+ */
227+ public static final String VERTICAL_CENTER = "VerticalCenter";
228+
229+ /**
230+ * Specifies the baseline of a component.
231+ *
232+ * @since 1.6
233+ */
234+ public static final String BASELINE = "Baseline";
235+
236+ /**
237+ * Specifies the width of a component's bounding rectangle.
238+ *
239+ * @since 1.6
240+ */
241+ public static final String WIDTH = "Width";
242+
243+ /**
244+ * Specifies the height of a component's bounding rectangle.
245+ *
246+ * @since 1.6
247+ */
248+ public static final String HEIGHT = "Height";
249+
250+ private static String[] ALL_HORIZONTAL = {WEST, WIDTH, EAST, HORIZONTAL_CENTER};
251+
252+ private static String[] ALL_VERTICAL = {NORTH, HEIGHT, SOUTH, VERTICAL_CENTER, BASELINE};
253+
254+ /**
255+ * A <code>Constraints</code> object holds the
256+ * constraints that govern the way a component's size and position
257+ * change in a container controlled by a <code>SpringLayout</code>.
258+ * A <code>Constraints</code> object is
259+ * like a <code>Rectangle</code>, in that it
260+ * has <code>x</code>, <code>y</code>,
261+ * <code>width</code>, and <code>height</code> properties.
262+ * In the <code>Constraints</code> object, however,
263+ * these properties have
264+ * <code>Spring</code> values instead of integers.
265+ * In addition,
266+ * a <code>Constraints</code> object
267+ * can be manipulated as four edges
268+ * -- north, south, east, and west --
269+ * using the <code>constraint</code> property.
270+ *
271+ * <p>
272+ * The following formulas are always true
273+ * for a <code>Constraints</code> object (here WEST and <code>x</code> are synonyms, as are and NORTH and <code>y</code>):
274+ *
275+ * <pre>
276+ * EAST = WEST + WIDTH
277+ * SOUTH = NORTH + HEIGHT
278+ * HORIZONTAL_CENTER = WEST + WIDTH/2
279+ * VERTICAL_CENTER = NORTH + HEIGHT/2
280+ * ABSOLUTE_BASELINE = NORTH + RELATIVE_BASELINE*
281+ * </pre>
282+ * <p>
283+ * For example, if you have specified the WIDTH and WEST (X) location
284+ * the EAST is calculated as WEST + WIDTH. If you instead specified
285+ * the WIDTH and EAST locations the WEST (X) location is then calculated
286+ * as EAST - WIDTH.
287+ * <p>
288+ * [RELATIVE_BASELINE is a private constraint that is set automatically when
289+ * the SpringLayout.Constraints(Component) constuctor is called or when
290+ * a constraints object is registered with a SpringLayout object.]
291+ * <p>
292+ * <b>Note</b>: In this document,
293+ * operators represent methods
294+ * in the <code>Spring</code> class.
295+ * For example, "a + b" is equal to
296+ * <code>Spring.sum(a, b)</code>,
297+ * and "a - b" is equal to
298+ * <code>Spring.sum(a, Spring.minus(b))</code>.
299+ * See the
300+ * {@link Spring <code>Spring</code> API documentation}
301+ * for further details
302+ * of spring arithmetic.
303+ *
304+ * <p>
305+ *
306+ * Because a <code>Constraints</code> object's properties --
307+ * representing its edges, size, and location -- can all be set
308+ * independently and yet are interrelated,
309+ * a <code>Constraints</code> object can become <em>over-constrained</em>.
310+ * For example, if the <code>WEST</code>, <code>WIDTH</code> and
311+ * <code>EAST</code> edges are all set, steps must be taken to ensure that
312+ * the first of the formulas above holds. To do this, the
313+ * <code>Constraints</code>
314+ * object throws away the <em>least recently set</em>
315+ * constraint so as to make the formulas hold.
316+ * @since 1.4
317+ */
318+ public static class Constraints {
319+ private Spring x;
320+ private Spring y;
321+ private Spring width;
322+ private Spring height;
323+ private Spring east;
324+ private Spring south;
325+ private Spring horizontalCenter;
326+ private Spring verticalCenter;
327+ private Spring baseline;
328+
329+ private List<String> horizontalHistory = new ArrayList<String>(2);
330+ private List<String> verticalHistory = new ArrayList<String>(2);
331+
332+ // Used for baseline calculations
333+ private Component c;
334+
335+ /**
336+ * Creates an empty <code>Constraints</code> object.
337+ */
338+ public Constraints() {
339+ }
340+
341+ /**
342+ * Creates a <code>Constraints</code> object with the
343+ * specified values for its
344+ * <code>x</code> and <code>y</code> properties.
345+ * The <code>height</code> and <code>width</code> springs
346+ * have <code>null</code> values.
347+ *
348+ * @param x the spring controlling the component's <em>x</em> value
349+ * @param y the spring controlling the component's <em>y</em> value
350+ */
351+ public Constraints(Spring x, Spring y) {
352+ setX(x);
353+ setY(y);
354+ }
355+
356+ /**
357+ * Creates a <code>Constraints</code> object with the
358+ * specified values for its
359+ * <code>x</code>, <code>y</code>, <code>width</code>,
360+ * and <code>height</code> properties.
361+ * Note: If the <code>SpringLayout</code> class
362+ * encounters <code>null</code> values in the
363+ * <code>Constraints</code> object of a given component,
364+ * it replaces them with suitable defaults.
365+ *
366+ * @param x the spring value for the <code>x</code> property
367+ * @param y the spring value for the <code>y</code> property
368+ * @param width the spring value for the <code>width</code> property
369+ * @param height the spring value for the <code>height</code> property
370+ */
371+ public Constraints(Spring x, Spring y, Spring width, Spring height) {
372+ setX(x);
373+ setY(y);
374+ setWidth(width);
375+ setHeight(height);
376+ }
377+
378+ /**
379+ * Creates a <code>Constraints</code> object with
380+ * suitable <code>x</code>, <code>y</code>, <code>width</code> and
381+ * <code>height</code> springs for component, <code>c</code>.
382+ * The <code>x</code> and <code>y</code> springs are constant
383+ * springs initialised with the component's location at
384+ * the time this method is called. The <code>width</code> and
385+ * <code>height</code> springs are special springs, created by
386+ * the <code>Spring.width()</code> and <code>Spring.height()</code>
387+ * methods, which track the size characteristics of the component
388+ * when they change.
389+ *
390+ * @param c the component whose characteristics will be reflected by this Constraints object
391+ * @throws NullPointerException if <code>c</code> is null.
392+ * @since 1.5
393+ */
394+ public Constraints(Component c) {
395+ this.c = c;
396+ setX(Spring.constant(c.getX()));
397+ setY(Spring.constant(c.getY()));
398+ setWidth(Spring.width(c));
399+ setHeight(Spring.height(c));
400+ }
401+
402+ private void pushConstraint(String name, Spring value, boolean horizontal) {
403+ boolean valid = true;
404+ List<String> history = horizontal ? horizontalHistory :
405+ verticalHistory;
406+ if (history.contains(name)) {
407+ history.remove(name);
408+ valid = false;
409+ } else if (history.size() == 2 && value != null) {
410+ history.remove(0);
411+ valid = false;
412+ }
413+ if (value != null) {
414+ history.add(name);
415+ }
416+ if (!valid) {
417+ String[] all = horizontal ? ALL_HORIZONTAL : ALL_VERTICAL;
418+ for (String s : all) {
419+ if (!history.contains(s)) {
420+ setConstraint(s, null);
421+ }
422+ }
423+ }
424+ }
425+
426+ private Spring sum(Spring s1, Spring s2) {
427+ return (s1 == null || s2 == null) ? null : Spring.sum(s1, s2);
428+ }
429+
430+ private Spring difference(Spring s1, Spring s2) {
431+ return (s1 == null || s2 == null) ? null : Spring.difference(s1, s2);
432+ }
433+
434+ private Spring scale(Spring s, float factor) {
435+ return (s == null) ? null : Spring.scale(s, factor);
436+ }
437+
438+ private int getBaselineFromHeight(int height) {
439+ if (height < 0) {
440+ // Bad Scott, Bad Scott!
441+ return -c.getBaseline(c.getPreferredSize().width,
442+ -height);
443+ }
444+ return c.getBaseline(c.getPreferredSize().width, height);
445+ }
446+
447+ private int getHeightFromBaseLine(int baseline) {
448+ Dimension prefSize = c.getPreferredSize();
449+ int prefHeight = prefSize.height;
450+ int prefBaseline = c.getBaseline(prefSize.width, prefHeight);
451+ if (prefBaseline == baseline) {
452+ // If prefBaseline < 0, then no baseline, assume preferred
453+ // height.
454+ // If prefBaseline == baseline, then specified baseline
455+ // matches preferred baseline, return preferred height
456+ return prefHeight;
457+ }
458+ // Valid baseline
459+ switch(c.getBaselineResizeBehavior()) {
460+ case CONSTANT_DESCENT:
461+ return prefHeight + (baseline - prefBaseline);
462+ case CENTER_OFFSET:
463+ return prefHeight + 2 * (baseline - prefBaseline);
464+ case CONSTANT_ASCENT:
465+ // Component baseline and specified baseline will NEVER
466+ // match, fall through to default
467+ default: // OTHER
468+ // No way to map from baseline to height.
469+ }
470+ return Integer.MIN_VALUE;
471+ }
472+
473+ private Spring heightToRelativeBaseline(Spring s) {
474+ return new Spring.SpringMap(s) {
475+ protected int map(int i) {
476+ return getBaselineFromHeight(i);
477+ }
478+
479+ protected int inv(int i) {
480+ return getHeightFromBaseLine(i);
481+ }
482+ };
483+ }
484+
485+ private Spring relativeBaselineToHeight(Spring s) {
486+ return new Spring.SpringMap(s) {
487+ protected int map(int i) {
488+ return getHeightFromBaseLine(i);
489+ }
490+
491+ protected int inv(int i) {
492+ return getBaselineFromHeight(i);
493+ }
494+ };
495+ }
496+
497+ private boolean defined(List history, String s1, String s2) {
498+ return history.contains(s1) && history.contains(s2);
499+ }
500+
501+ /**
502+ * Sets the <code>x</code> property,
503+ * which controls the <code>x</code> value
504+ * of a component's location.
505+ *
506+ * @param x the spring controlling the <code>x</code> value
507+ * of a component's location
508+ *
509+ * @see #getX
510+ * @see SpringLayout.Constraints
511+ */
512+ public void setX(Spring x) {
513+ this.x = x;
514+ pushConstraint(WEST, x, true);
515+ }
516+
517+ /**
518+ * Returns the value of the <code>x</code> property.
519+ *
520+ * @return the spring controlling the <code>x</code> value
521+ * of a component's location
522+ *
523+ * @see #setX
524+ * @see SpringLayout.Constraints
525+ */
526+ public Spring getX() {
527+ if (x == null) {
528+ if (defined(horizontalHistory, EAST, WIDTH)) {
529+ x = difference(east, width);
530+ } else if (defined(horizontalHistory, HORIZONTAL_CENTER, WIDTH)) {
531+ x = difference(horizontalCenter, scale(width, 0.5f));
532+ } else if (defined(horizontalHistory, HORIZONTAL_CENTER, EAST)) {
533+ x = difference(scale(horizontalCenter, 2f), east);
534+ }
535+ }
536+ return x;
537+ }
538+
539+ /**
540+ * Sets the <code>y</code> property,
541+ * which controls the <code>y</code> value
542+ * of a component's location.
543+ *
544+ * @param y the spring controlling the <code>y</code> value
545+ * of a component's location
546+ *
547+ * @see #getY
548+ * @see SpringLayout.Constraints
549+ */
550+ public void setY(Spring y) {
551+ this.y = y;
552+ pushConstraint(NORTH, y, false);
553+ }
554+
555+ /**
556+ * Returns the value of the <code>y</code> property.
557+ *
558+ * @return the spring controlling the <code>y</code> value
559+ * of a component's location
560+ *
561+ * @see #setY
562+ * @see SpringLayout.Constraints
563+ */
564+ public Spring getY() {
565+ if (y == null) {
566+ if (defined(verticalHistory, SOUTH, HEIGHT)) {
567+ y = difference(south, height);
568+ } else if (defined(verticalHistory, VERTICAL_CENTER, HEIGHT)) {
569+ y = difference(verticalCenter, scale(height, 0.5f));
570+ } else if (defined(verticalHistory, VERTICAL_CENTER, SOUTH)) {
571+ y = difference(scale(verticalCenter, 2f), south);
572+ } else if (defined(verticalHistory, BASELINE, HEIGHT)) {
573+ y = difference(baseline, heightToRelativeBaseline(height));
574+ } else if (defined(verticalHistory, BASELINE, SOUTH)) {
575+ y = scale(difference(baseline, heightToRelativeBaseline(south)), 2f);
576+/*
577+ } else if (defined(verticalHistory, BASELINE, VERTICAL_CENTER)) {
578+ y = scale(difference(baseline, heightToRelativeBaseline(scale(verticalCenter, 2))), 1f/(1-2*0.5f));
579+*/
580+ }
581+ }
582+ return y;
583+ }
584+
585+ /**
586+ * Sets the <code>width</code> property,
587+ * which controls the width of a component.
588+ *
589+ * @param width the spring controlling the width of this
590+ * <code>Constraints</code> object
591+ *
592+ * @see #getWidth
593+ * @see SpringLayout.Constraints
594+ */
595+ public void setWidth(Spring width) {
596+ this.width = width;
597+ pushConstraint(WIDTH, width, true);
598+ }
599+
600+ /**
601+ * Returns the value of the <code>width</code> property.
602+ *
603+ * @return the spring controlling the width of a component
604+ *
605+ * @see #setWidth
606+ * @see SpringLayout.Constraints
607+ */
608+ public Spring getWidth() {
609+ if (width == null) {
610+ if (horizontalHistory.contains(EAST)) {
611+ width = difference(east, getX());
612+ } else if (horizontalHistory.contains(HORIZONTAL_CENTER)) {
613+ width = scale(difference(horizontalCenter, getX()), 2f);
614+ }
615+ }
616+ return width;
617+ }
618+
619+ /**
620+ * Sets the <code>height</code> property,
621+ * which controls the height of a component.
622+ *
623+ * @param height the spring controlling the height of this <code>Constraints</code>
624+ * object
625+ *
626+ * @see #getHeight
627+ * @see SpringLayout.Constraints
628+ */
629+ public void setHeight(Spring height) {
630+ this.height = height;
631+ pushConstraint(HEIGHT, height, false);
632+ }
633+
634+ /**
635+ * Returns the value of the <code>height</code> property.
636+ *
637+ * @return the spring controlling the height of a component
638+ *
639+ * @see #setHeight
640+ * @see SpringLayout.Constraints
641+ */
642+ public Spring getHeight() {
643+ if (height == null) {
644+ if (verticalHistory.contains(SOUTH)) {
645+ height = difference(south, getY());
646+ } else if (verticalHistory.contains(VERTICAL_CENTER)) {
647+ height = scale(difference(verticalCenter, getY()), 2f);
648+ } else if (verticalHistory.contains(BASELINE)) {
649+ height = relativeBaselineToHeight(difference(baseline, getY()));
650+ }
651+ }
652+ return height;
653+ }
654+
655+ private void setEast(Spring east) {
656+ this.east = east;
657+ pushConstraint(EAST, east, true);
658+ }
659+
660+ private Spring getEast() {
661+ if (east == null) {
662+ east = sum(getX(), getWidth());
663+ }
664+ return east;
665+ }
666+
667+ private void setSouth(Spring south) {
668+ this.south = south;
669+ pushConstraint(SOUTH, south, false);
670+ }
671+
672+ private Spring getSouth() {
673+ if (south == null) {
674+ south = sum(getY(), getHeight());
675+ }
676+ return south;
677+ }
678+
679+ private Spring getHorizontalCenter() {
680+ if (horizontalCenter == null) {
681+ horizontalCenter = sum(getX(), scale(getWidth(), 0.5f));
682+ }
683+ return horizontalCenter;
684+ }
685+
686+ private void setHorizontalCenter(Spring horizontalCenter) {
687+ this.horizontalCenter = horizontalCenter;
688+ pushConstraint(HORIZONTAL_CENTER, horizontalCenter, true);
689+ }
690+
691+ private Spring getVerticalCenter() {
692+ if (verticalCenter == null) {
693+ verticalCenter = sum(getY(), scale(getHeight(), 0.5f));
694+ }
695+ return verticalCenter;
696+ }
697+
698+ private void setVerticalCenter(Spring verticalCenter) {
699+ this.verticalCenter = verticalCenter;
700+ pushConstraint(VERTICAL_CENTER, verticalCenter, false);
701+ }
702+
703+ private Spring getBaseline() {
704+ if (baseline == null) {
705+ baseline = sum(getY(), heightToRelativeBaseline(getHeight()));
706+ }
707+ return baseline;
708+ }
709+
710+ private void setBaseline(Spring baseline) {
711+ this.baseline = baseline;
712+ pushConstraint(BASELINE, baseline, false);
713+ }
714+
715+ /**
716+ * Sets the spring controlling the specified edge.
717+ * The edge must have one of the following values:
718+ * <code>SpringLayout.NORTH</code>,
719+ * <code>SpringLayout.SOUTH</code>,
720+ * <code>SpringLayout.EAST</code>,
721+ * <code>SpringLayout.WEST</code>,
722+ * <code>SpringLayout.HORIZONTAL_CENTER</code>,
723+ * <code>SpringLayout.VERTICAL_CENTER</code>,
724+ * <code>SpringLayout.BASELINE</code>,
725+ * <code>SpringLayout.WIDTH</code> or
726+ * <code>SpringLayout.HEIGHT</code>.
727+ * For any other <code>String</code> value passed as the edge,
728+ * no action is taken. For a <code>null</code> edge, a
729+ * <code>NullPointerException</code> is thrown.
730+ * <p/>
731+ * <b>Note:</b> This method can affect {@code x} and {@code y} values
732+ * previously set for this {@code Constraints}.
733+ *
734+ * @param edgeName the edge to be set
735+ * @param s the spring controlling the specified edge
736+ *
737+ * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
738+ *
739+ * @see #getConstraint
740+ * @see #NORTH
741+ * @see #SOUTH
742+ * @see #EAST
743+ * @see #WEST
744+ * @see #HORIZONTAL_CENTER
745+ * @see #VERTICAL_CENTER
746+ * @see #BASELINE
747+ * @see #WIDTH
748+ * @see #HEIGHT
749+ * @see SpringLayout.Constraints
750+ */
751+ public void setConstraint(String edgeName, Spring s) {
752+ edgeName = edgeName.intern();
753+ if (edgeName == WEST) {
754+ setX(s);
755+ } else if (edgeName == NORTH) {
756+ setY(s);
757+ } else if (edgeName == EAST) {
758+ setEast(s);
759+ } else if (edgeName == SOUTH) {
760+ setSouth(s);
761+ } else if (edgeName == HORIZONTAL_CENTER) {
762+ setHorizontalCenter(s);
763+ } else if (edgeName == WIDTH) {
764+ setWidth(s);
765+ } else if (edgeName == HEIGHT) {
766+ setHeight(s);
767+ } else if (edgeName == VERTICAL_CENTER) {
768+ setVerticalCenter(s);
769+ } else if (edgeName == BASELINE) {
770+ setBaseline(s);
771+ }
772+ }
773+
774+ /**
775+ * Returns the value of the specified edge, which may be
776+ * a derived value, or even <code>null</code>.
777+ * The edge must have one of the following values:
778+ * <code>SpringLayout.NORTH</code>,
779+ * <code>SpringLayout.SOUTH</code>,
780+ * <code>SpringLayout.EAST</code>,
781+ * <code>SpringLayout.WEST</code>,
782+ * <code>SpringLayout.HORIZONTAL_CENTER</code>,
783+ * <code>SpringLayout.VERTICAL_CENTER</code>,
784+ * <code>SpringLayout.BASELINE</code>,
785+ * <code>SpringLayout.WIDTH</code> or
786+ * <code>SpringLayout.HEIGHT</code>.
787+ * For any other <code>String</code> value passed as the edge,
788+ * <code>null</code> will be returned. Throws
789+ * <code>NullPointerException</code> for a <code>null</code> edge.
790+ *
791+ * @param edgeName the edge whose value
792+ * is to be returned
793+ *
794+ * @return the spring controlling the specified edge, may be <code>null</code>
795+ *
796+ * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
797+ *
798+ * @see #setConstraint
799+ * @see #NORTH
800+ * @see #SOUTH
801+ * @see #EAST
802+ * @see #WEST
803+ * @see #HORIZONTAL_CENTER
804+ * @see #VERTICAL_CENTER
805+ * @see #BASELINE
806+ * @see #WIDTH
807+ * @see #HEIGHT
808+ * @see SpringLayout.Constraints
809+ */
810+ public Spring getConstraint(String edgeName) {
811+ edgeName = edgeName.intern();
812+ return (edgeName == WEST) ? getX() :
813+ (edgeName == NORTH) ? getY() :
814+ (edgeName == EAST) ? getEast() :
815+ (edgeName == SOUTH) ? getSouth() :
816+ (edgeName == WIDTH) ? getWidth() :
817+ (edgeName == HEIGHT) ? getHeight() :
818+ (edgeName == HORIZONTAL_CENTER) ? getHorizontalCenter() :
819+ (edgeName == VERTICAL_CENTER) ? getVerticalCenter() :
820+ (edgeName == BASELINE) ? getBaseline() :
821+ null;
822+ }
823+
824+ /*pp*/ void reset() {
825+ Spring[] allSprings = {x, y, width, height, east, south,
826+ horizontalCenter, verticalCenter, baseline};
827+ for (Spring s : allSprings) {
828+ if (s != null) {
829+ s.setValue(Spring.UNSET);
830+ }
831+ }
832+ }
833+ }
834+
835+ private static class SpringProxy extends Spring {
836+ private String edgeName;
837+ private Component c;
838+ private SpringLayout l;
839+
840+ public SpringProxy(String edgeName, Component c, SpringLayout l) {
841+ this.edgeName = edgeName;
842+ this.c = c;
843+ this.l = l;
844+ }
845+
846+ private Spring getConstraint() {
847+ return l.getConstraints(c).getConstraint(edgeName);
848+ }
849+
850+ public int getMinimumValue() {
851+ return getConstraint().getMinimumValue();
852+ }
853+
854+ public int getPreferredValue() {
855+ return getConstraint().getPreferredValue();
856+ }
857+
858+ public int getMaximumValue() {
859+ return getConstraint().getMaximumValue();
860+ }
861+
862+ public int getValue() {
863+ return getConstraint().getValue();
864+ }
865+
866+ public void setValue(int size) {
867+ getConstraint().setValue(size);
868+ }
869+
870+ /*pp*/ boolean isCyclic(SpringLayout l) {
871+ return l.isCyclic(getConstraint());
872+ }
873+
874+ public String toString() {
875+ return "SpringProxy for " + edgeName + " edge of " + c.getName() + ".";
876+ }
877+ }
878+
879+ /**
880+ * Constructs a new <code>SpringLayout</code>.
881+ */
882+ public SpringLayout() {}
883+
884+ private void resetCyclicStatuses() {
885+ cyclicSprings = new HashSet<Spring>();
886+ acyclicSprings = new HashSet<Spring>();
887+ }
888+
889+ private void setParent(Container p) {
890+ resetCyclicStatuses();
891+ Constraints pc = getConstraints(p);
892+
893+ pc.setX(Spring.constant(0));
894+ pc.setY(Spring.constant(0));
895+ // The applyDefaults() method automatically adds width and
896+ // height springs that delegate their calculations to the
897+ // getMinimumSize(), getPreferredSize() and getMaximumSize()
898+ // methods of the relevant component. In the case of the
899+ // parent this will cause an infinite loop since these
900+ // methods, in turn, delegate their calculations to the
901+ // layout manager. Check for this case and replace the
902+ // the springs that would cause this problem with a
903+ // constant springs that supply default values.
904+ Spring width = pc.getWidth();
905+ if (width instanceof Spring.WidthSpring && ((Spring.WidthSpring)width).c == p) {
906+ pc.setWidth(Spring.constant(0, 0, Integer.MAX_VALUE));
907+ }
908+ Spring height = pc.getHeight();
909+ if (height instanceof Spring.HeightSpring && ((Spring.HeightSpring)height).c == p) {
910+ pc.setHeight(Spring.constant(0, 0, Integer.MAX_VALUE));
911+ }
912+ }
913+
914+ /*pp*/ boolean isCyclic(Spring s) {
915+ if (s == null) {
916+ return false;
917+ }
918+ if (cyclicSprings.contains(s)) {
919+ return true;
920+ }
921+ if (acyclicSprings.contains(s)) {
922+ return false;
923+ }
924+ cyclicSprings.add(s);
925+ boolean result = s.isCyclic(this);
926+ if (!result) {
927+ acyclicSprings.add(s);
928+ cyclicSprings.remove(s);
929+ }
930+ else {
931+ System.err.println(s + " is cyclic. ");
932+ }
933+ return result;
934+ }
935+
936+ private Spring abandonCycles(Spring s) {
937+ return isCyclic(s) ? cyclicReference : s;
938+ }
939+
940+ // LayoutManager methods.
941+
942+ /**
943+ * Has no effect,
944+ * since this layout manager does not
945+ * use a per-component string.
946+ */
947+ public void addLayoutComponent(String name, Component c) {}
948+
949+ /**
950+ * Removes the constraints associated with the specified component.
951+ *
952+ * @param c the component being removed from the container
953+ */
954+ public void removeLayoutComponent(Component c) {
955+ componentConstraints.remove(c);
956+ }
957+
958+ private static Dimension addInsets(int width, int height, Container p) {
959+ Insets i = p.getInsets();
960+ return new Dimension(width + i.left + i.right, height + i.top + i.bottom);
961+ }
962+
963+ public Dimension minimumLayoutSize(Container parent) {
964+ setParent(parent);
965+ Constraints pc = getConstraints(parent);
966+ return addInsets(abandonCycles(pc.getWidth()).getMinimumValue(),
967+ abandonCycles(pc.getHeight()).getMinimumValue(),
968+ parent);
969+ }
970+
971+ public Dimension preferredLayoutSize(Container parent) {
972+ setParent(parent);
973+ Constraints pc = getConstraints(parent);
974+ return addInsets(abandonCycles(pc.getWidth()).getPreferredValue(),
975+ abandonCycles(pc.getHeight()).getPreferredValue(),
976+ parent);
977+ }
978+
979+ // LayoutManager2 methods.
980+
981+ public Dimension maximumLayoutSize(Container parent) {
982+ setParent(parent);
983+ Constraints pc = getConstraints(parent);
984+ return addInsets(abandonCycles(pc.getWidth()).getMaximumValue(),
985+ abandonCycles(pc.getHeight()).getMaximumValue(),
986+ parent);
987+ }
988+
989+ /**
990+ * If <code>constraints</code> is an instance of
991+ * <code>SpringLayout.Constraints</code>,
992+ * associates the constraints with the specified component.
993+ * <p>
994+ * @param component the component being added
995+ * @param constraints the component's constraints
996+ *
997+ * @see SpringLayout.Constraints
998+ */
999+ public void addLayoutComponent(Component component, Object constraints) {
1000+ if (constraints instanceof Constraints) {
1001+ putConstraints(component, (Constraints)constraints);
1002+ }
1003+ }
1004+
1005+ /**
1006+ * Returns 0.5f (centered).
1007+ */
1008+ public float getLayoutAlignmentX(Container p) {
1009+ return 0.5f;
1010+ }
1011+
1012+ /**
1013+ * Returns 0.5f (centered).
1014+ */
1015+ public float getLayoutAlignmentY(Container p) {
1016+ return 0.5f;
1017+ }
1018+
1019+ public void invalidateLayout(Container p) {}
1020+
1021+ // End of LayoutManger2 methods
1022+
1023+ /**
1024+ * Links edge <code>e1</code> of component <code>c1</code> to
1025+ * edge <code>e2</code> of component <code>c2</code>,
1026+ * with a fixed distance between the edges. This
1027+ * constraint will cause the assignment
1028+ * <pre>
1029+ * value(e1, c1) = value(e2, c2) + pad</pre>
1030+ * to take place during all subsequent layout operations.
1031+ * <p>
1032+ * @param e1 the edge of the dependent
1033+ * @param c1 the component of the dependent
1034+ * @param pad the fixed distance between dependent and anchor
1035+ * @param e2 the edge of the anchor
1036+ * @param c2 the component of the anchor
1037+ *
1038+ * @see #putConstraint(String, Component, Spring, String, Component)
1039+ */
1040+ public void putConstraint(String e1, Component c1, int pad, String e2, Component c2) {
1041+ putConstraint(e1, c1, Spring.constant(pad), e2, c2);
1042+ }
1043+
1044+ /**
1045+ * Links edge <code>e1</code> of component <code>c1</code> to
1046+ * edge <code>e2</code> of component <code>c2</code>. As edge
1047+ * <code>(e2, c2)</code> changes value, edge <code>(e1, c1)</code> will
1048+ * be calculated by taking the (spring) sum of <code>(e2, c2)</code>
1049+ * and <code>s</code>.
1050+ * Each edge must have one of the following values:
1051+ * <code>SpringLayout.NORTH</code>,
1052+ * <code>SpringLayout.SOUTH</code>,
1053+ * <code>SpringLayout.EAST</code>,
1054+ * <code>SpringLayout.WEST</code>,
1055+ * <code>SpringLayout.VERTICAL_CENTER</code>,
1056+ * <code>SpringLayout.HORIZONTAL_CENTER</code> or
1057+ * <code>SpringLayout.BASELINE</code>.
1058+ * <p>
1059+ * @param e1 the edge of the dependent
1060+ * @param c1 the component of the dependent
1061+ * @param s the spring linking dependent and anchor
1062+ * @param e2 the edge of the anchor
1063+ * @param c2 the component of the anchor
1064+ *
1065+ * @see #putConstraint(String, Component, int, String, Component)
1066+ * @see #NORTH
1067+ * @see #SOUTH
1068+ * @see #EAST
1069+ * @see #WEST
1070+ * @see #VERTICAL_CENTER
1071+ * @see #HORIZONTAL_CENTER
1072+ * @see #BASELINE
1073+ */
1074+ public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2) {
1075+ putConstraint(e1, c1, Spring.sum(s, getConstraint(e2, c2)));
1076+ }
1077+
1078+ private void putConstraint(String e, Component c, Spring s) {
1079+ if (s != null) {
1080+ getConstraints(c).setConstraint(e, s);
1081+ }
1082+ }
1083+
1084+ private Constraints applyDefaults(Component c, Constraints constraints) {
1085+ if (constraints == null) {
1086+ constraints = new Constraints();
1087+ }
1088+ if (constraints.c == null) {
1089+ constraints.c = c;
1090+ }
1091+ if (constraints.horizontalHistory.size() < 2) {
1092+ applyDefaults(constraints, WEST, Spring.constant(0), WIDTH,
1093+ Spring.width(c), constraints.horizontalHistory);
1094+ }
1095+ if (constraints.verticalHistory.size() < 2) {
1096+ applyDefaults(constraints, NORTH, Spring.constant(0), HEIGHT,
1097+ Spring.height(c), constraints.verticalHistory);
1098+ }
1099+ return constraints;
1100+ }
1101+
1102+ private void applyDefaults(Constraints constraints, String name1,
1103+ Spring spring1, String name2, Spring spring2,
1104+ List<String> history) {
1105+ if (history.size() == 0) {
1106+ constraints.setConstraint(name1, spring1);
1107+ constraints.setConstraint(name2, spring2);
1108+ } else {
1109+ // At this point there must be exactly one constraint defined already.
1110+ // Check width/height first.
1111+ if (constraints.getConstraint(name2) == null) {
1112+ constraints.setConstraint(name2, spring2);
1113+ } else {
1114+ // If width/height is already defined, install a default for x/y.
1115+ constraints.setConstraint(name1, spring1);
1116+ }
1117+ // Either way, leave the user's constraint topmost on the stack.
1118+ Collections.rotate(history, 1);
1119+ }
1120+ }
1121+
1122+ private void putConstraints(Component component, Constraints constraints) {
1123+ componentConstraints.put(component, applyDefaults(component, constraints));
1124+ }
1125+
1126+ /**
1127+ * Returns the constraints for the specified component.
1128+ * Note that,
1129+ * unlike the <code>GridBagLayout</code>
1130+ * <code>getConstraints</code> method,
1131+ * this method does not clone constraints.
1132+ * If no constraints
1133+ * have been associated with this component,
1134+ * this method
1135+ * returns a default constraints object positioned at
1136+ * 0,0 relative to the parent's Insets and its width/height
1137+ * constrained to the minimum, maximum, and preferred sizes of the
1138+ * component. The size characteristics
1139+ * are not frozen at the time this method is called;
1140+ * instead this method returns a constraints object
1141+ * whose characteristics track the characteristics
1142+ * of the component as they change.
1143+ *
1144+ * @param c the component whose constraints will be returned
1145+ *
1146+ * @return the constraints for the specified component
1147+ */
1148+ public Constraints getConstraints(Component c) {
1149+ Constraints result = componentConstraints.get(c);
1150+ if (result == null) {
1151+ if (c instanceof javax.swing.JComponent) {
1152+ Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class);
1153+ if (cp instanceof Constraints) {
1154+ return applyDefaults(c, (Constraints)cp);
1155+ }
1156+ }
1157+ result = new Constraints();
1158+ putConstraints(c, result);
1159+ }
1160+ return result;
1161+ }
1162+
1163+ /**
1164+ * Returns the spring controlling the distance between
1165+ * the specified edge of
1166+ * the component and the top or left edge of its parent. This
1167+ * method, instead of returning the current binding for the
1168+ * edge, returns a proxy that tracks the characteristics
1169+ * of the edge even if the edge is subsequently rebound.
1170+ * Proxies are intended to be used in builder envonments
1171+ * where it is useful to allow the user to define the
1172+ * constraints for a layout in any order. Proxies do, however,
1173+ * provide the means to create cyclic dependencies amongst
1174+ * the constraints of a layout. Such cycles are detected
1175+ * internally by <code>SpringLayout</code> so that
1176+ * the layout operation always terminates.
1177+ *
1178+ * @param edgeName must be one of
1179+ * <code>SpringLayout.NORTH</code>,
1180+ * <code>SpringLayout.SOUTH</code>,
1181+ * <code>SpringLayout.EAST</code>,
1182+ * <code>SpringLayout.WEST</code>,
1183+ * <code>SpringLayout.VERTICAL_CENTER</code>,
1184+ * <code>SpringLayout.HORIZONTAL_CENTER</code> or
1185+ * <code>SpringLayout.BASELINE</code>
1186+ * @param c the component whose edge spring is desired
1187+ *
1188+ * @return a proxy for the spring controlling the distance between the
1189+ * specified edge and the top or left edge of its parent
1190+ *
1191+ * @see #NORTH
1192+ * @see #SOUTH
1193+ * @see #EAST
1194+ * @see #WEST
1195+ * @see #VERTICAL_CENTER
1196+ * @see #HORIZONTAL_CENTER
1197+ * @see #BASELINE
1198+ */
1199+ public Spring getConstraint(String edgeName, Component c) {
1200+ // The interning here is unnecessary; it was added for efficiency.
1201+ edgeName = edgeName.intern();
1202+ return new SpringProxy(edgeName, c, this);
1203+ }
1204+
1205+ public void layoutContainer(Container parent) {
1206+ setParent(parent);
1207+
1208+ int n = parent.getComponentCount();
1209+ getConstraints(parent).reset();
1210+ for (int i = 0 ; i < n ; i++) {
1211+ getConstraints(parent.getComponent(i)).reset();
1212+ }
1213+
1214+ Insets insets = parent.getInsets();
1215+ Constraints pc = getConstraints(parent);
1216+ abandonCycles(pc.getX()).setValue(0);
1217+ abandonCycles(pc.getY()).setValue(0);
1218+ abandonCycles(pc.getWidth()).setValue(parent.getWidth() -
1219+ insets.left - insets.right);
1220+ abandonCycles(pc.getHeight()).setValue(parent.getHeight() -
1221+ insets.top - insets.bottom);
1222+
1223+ for (int i = 0 ; i < n ; i++) {
1224+ Component c = parent.getComponent(i);
1225+ Constraints cc = getConstraints(c);
1226+ int x = abandonCycles(cc.getX()).getValue();
1227+ int y = abandonCycles(cc.getY()).getValue();
1228+ int width = abandonCycles(cc.getWidth()).getValue();
1229+ int height = abandonCycles(cc.getHeight()).getValue();
1230+ c.setBounds(insets.left + x, insets.top + y, width, height);
1231+ }
1232+ }
1233+}