1. /*
  2. * @(#)SpringLayout.java 1.19 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.*;
  9. import java.util.*;
  10. /**
  11. * A <code>SpringLayout</code> lays out the children of its associated container
  12. * according to a set of constraints.
  13. * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a>
  14. * in <em>The Java Tutorial</em> for examples of using
  15. * <code>SpringLayout</code>.
  16. *
  17. * <p>
  18. * Each constraint,
  19. * represented by a <code>Spring</code> object,
  20. * controls the vertical or horizontal distance
  21. * between two component edges.
  22. * The edges can belong to
  23. * any child of the container,
  24. * or to the container itself.
  25. * For example,
  26. * the allowable width of a component
  27. * can be expressed using a constraint
  28. * that controls the distance between the west (left) and east (right)
  29. * edges of the component.
  30. * The allowable <em>y</em> coordinates for a component
  31. * can be expressed by constraining the distance between
  32. * the north (top) edge of the component
  33. * and the north edge of its container.
  34. *
  35. * <P>
  36. * Every child of a <code>SpringLayout</code>-controlled container,
  37. * as well as the container itself,
  38. * has exactly one set of constraints
  39. * associated with it.
  40. * These constraints are represented by
  41. * a <code>SpringLayout.Constraints</code> object.
  42. * By default,
  43. * <code>SpringLayout</code> creates constraints
  44. * that make their associated component
  45. * have the minimum, preferred, and maximum sizes
  46. * returned by the component's
  47. * {@link java.awt.Component#getMinimumSize},
  48. * {@link java.awt.Component#getPreferredSize}, and
  49. * {@link java.awt.Component#getMaximumSize}
  50. * methods. The <em>x</em> and <em>y</em> positions are initially not
  51. * constrained, so that until you constrain them the <code>Component</code>
  52. * will be positioned at 0,0 relative to the <code>Insets</code> of the
  53. * parent <code>Container</code>.
  54. *
  55. * <p>
  56. * You can change
  57. * a component's constraints in several ways.
  58. * You can
  59. * use one of the
  60. * {@link #putConstraint putConstraint}
  61. * methods
  62. * to establish a spring
  63. * linking the edges of two components within the same container.
  64. * Or you can get the appropriate <code>SpringLayout.Constraints</code>
  65. * object using
  66. * {@link #getConstraints getConstraints}
  67. * and then modify one or more of its springs.
  68. * Or you can get the spring for a particular edge of a component
  69. * using {@link #getConstraint getConstraint},
  70. * and modify it.
  71. * You can also associate
  72. * your own <code>SpringLayout.Constraints</code> object
  73. * with a component by specifying the constraints object
  74. * when you add the component to its container
  75. * (using
  76. * {@link Container#add(Component, Object)}).
  77. *
  78. * <p>
  79. * The <code>Spring</code> object representing each constraint
  80. * has a minimum, preferred, maximum, and current value.
  81. * The current value of the spring
  82. * is somewhere between the minimum and maximum values,
  83. * according to the formula given in the
  84. * {@link Spring#sum} method description.
  85. * When the minimum, preferred, and maximum values are the same,
  86. * the current value is always equal to them;
  87. * this inflexible spring is called a <em>strut</em>.
  88. * You can create struts using the factory method
  89. * {@link Spring#constant(int)}.
  90. * The <code>Spring</code> class also provides factory methods
  91. * for creating other kinds of springs,
  92. * including springs that depend on other springs.
  93. *
  94. * <p>
  95. * In a <code>SpringLayout</code>, the position of each edge is dependent on
  96. * the position of just one other edge. If a constraint is subsequently added
  97. * to create a new binding for an edge, the previous binding is discarded
  98. * and the edge remains dependent on a single edge.
  99. * Springs should only be attached
  100. * between edges of the container and its immediate children; the behavior
  101. * of the <code>SpringLayout</code> when presented with constraints linking
  102. * the edges of components from different containers (either internal or
  103. * external) is undefined.
  104. *
  105. * <h3>
  106. * SpringLayout vs. Other Layout Managers
  107. * </h3>
  108. *
  109. * <blockquote>
  110. * <hr>
  111. * <strong>Note:</strong>
  112. * Unlike many layout managers,
  113. * <code>SpringLayout</code> doesn't automatically set the location of
  114. * the components it manages.
  115. * If you hand-code a GUI that uses <code>SpringLayout</code>,
  116. * remember to initialize component locations by constraining the west/east
  117. * and north/south locations.
  118. * <p>
  119. * Depending on the constraints you use,
  120. * you may also need to set the size of the container explicitly.
  121. * <hr>
  122. * </blockquote>
  123. *
  124. * <p>
  125. * Despite the simplicity of <code>SpringLayout</code>,
  126. * it can emulate the behavior of most other layout managers.
  127. * For some features,
  128. * such as the line breaking provided by <code>FlowLayout</code>,
  129. * you'll need to
  130. * create a special-purpose subclass of the <code>Spring</code> class.
  131. *
  132. * <p>
  133. * <code>SpringLayout</code> also provides a way to solve
  134. * many of the difficult layout
  135. * problems that cannot be solved by nesting combinations
  136. * of <code>Box</code>es. That said, <code>SpringLayout</code> honors the
  137. * <code>LayoutManager2</code> contract correctly and so can be nested with
  138. * other layout managers -- a technique that can be preferable to
  139. * creating the constraints implied by the other layout managers.
  140. * <p>
  141. * The asymptotic complexity of the layout operation of a <code>SpringLayout</code>
  142. * is linear in the number of constraints (and/or components).
  143. * <p>
  144. * <strong>Warning:</strong>
  145. * Serialized objects of this class will not be compatible with
  146. * future Swing releases. The current serialization support is
  147. * appropriate for short term storage or RMI between applications running
  148. * the same version of Swing. As of 1.4, support for long term storage
  149. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  150. * has been added to the <code>java.beans</code> package.
  151. * Please see {@link java.beans.XMLEncoder}.
  152. *
  153. * @see Spring
  154. * @see SpringLayout.Constraints
  155. *
  156. * @version 1.19 12/19/03
  157. * @author Philip Milne
  158. * @author Joe Winchester
  159. * @since 1.4
  160. */
  161. public class SpringLayout implements LayoutManager2 {
  162. private Map componentConstraints = new HashMap();
  163. private Spring cyclicReference = Spring.constant(Spring.UNSET);
  164. private Set cyclicSprings;
  165. private Set acyclicSprings;
  166. /**
  167. * Specifies the top edge of a component's bounding rectangle.
  168. */
  169. public static final String NORTH = "North";
  170. /**
  171. * Specifies the bottom edge of a component's bounding rectangle.
  172. */
  173. public static final String SOUTH = "South";
  174. /**
  175. * Specifies the right edge of a component's bounding rectangle.
  176. */
  177. public static final String EAST = "East";
  178. /**
  179. * Specifies the left edge of a component's bounding rectangle.
  180. */
  181. public static final String WEST = "West";
  182. /**
  183. * A <code>Constraints</code> object holds the
  184. * constraints that govern the way a component's size and position
  185. * change in a container controlled by a <code>SpringLayout</code>.
  186. * A <code>Constraints</code> object is
  187. * like a <code>Rectangle</code>, in that it
  188. * has <code>x</code>, <code>y</code>,
  189. * <code>width</code>, and <code>height</code> properties.
  190. * In the <code>Constraints</code> object, however,
  191. * these properties have
  192. * <code>Spring</code> values instead of integers.
  193. * In addition,
  194. * a <code>Constraints</code> object
  195. * can be manipulated as four edges
  196. * -- north, south, east, and west --
  197. * using the <code>constraint</code> property.
  198. *
  199. * <p>
  200. * The following formulas are always true
  201. * for a <code>Constraints</code> object:
  202. *
  203. * <pre>
  204. * west = x
  205. * north = y
  206. * east = x + width
  207. * south = y + height</pre>
  208. *
  209. * <b>Note</b>: In this document,
  210. * operators represent methods
  211. * in the <code>Spring</code> class.
  212. * For example, "a + b" is equal to
  213. * <code>Spring.sum(a, b)</code>,
  214. * and "a - b" is equal to
  215. * <code>Spring.sum(a, Spring.minus(b))</code>.
  216. * See the
  217. * {@link Spring Spring</code> API documentation<code>}
  218. * for further details
  219. * of spring arithmetic.
  220. *
  221. * <p>
  222. *
  223. * Because a <code>Constraints</code> object's properties --
  224. * representing its edges, size, and location -- can all be set
  225. * independently and yet are interrelated,
  226. * the object can become <em>over-constrained</em>.
  227. * For example,
  228. * if both the <code>x</code> and <code>width</code>
  229. * properties are set
  230. * and then the east edge is set,
  231. * the object is over-constrained horizontally.
  232. * When this happens, one of the values
  233. * (in this case, the <code>x</code> property)
  234. * automatically changes so
  235. * that the formulas still hold.
  236. *
  237. * <p>
  238. * The following table shows which value changes
  239. * when a <code>Constraints</code> object
  240. * is over-constrained horizontally.
  241. *
  242. * <p>
  243. *
  244. * <table border=1 summary="Shows which value changes when a Constraints object is over-constrained horizontally">
  245. * <tr>
  246. * <th valign=top>Value Being Set<br>(method used)</th>
  247. * <th valign=top>Result When Over-Constrained Horizontally<br>
  248. * (<code>x</code>, <code>width</code>, and the east edge are all non-<code>null</code>)</th>
  249. * </tr>
  250. * <tr>
  251. * <td><code>x</code> or the west edge <br>(<code>setX</code> or <code>setConstraint</code>)</td>
  252. * <td><code>width</code> value is automatically set to <code>east - x</code>.</td>
  253. * </tr>
  254. * <tr>
  255. * <td><code>width</code><br>(<code>setWidth</code>)</td>
  256. * <td>east edge's value is automatically set to <code>x + width</code>.</td>
  257. * </tr>
  258. * <tr>
  259. * <td>east edge<br>(<code>setConstraint</code>)</td>
  260. * <td><code>x</code> value is automatically set to <code>east - width</code>.</td>
  261. * </tr>
  262. * </table>
  263. *
  264. * <p>
  265. * The rules for the vertical properties are similar:
  266. * <p>
  267. *
  268. * <table border=1 summary="Shows which value changes when a Constraints object is over-constrained vertically">
  269. * <tr>
  270. * <th valign=top>Value Being Set<br>(method used)</th>
  271. * <th valign=top>Result When Over-Constrained Vertically<br>(<code>y</code>, <code>height</code>, and the south edge are all non-<code>null</code>)</th>
  272. * </tr>
  273. * <tr>
  274. * <td><code>y</code> or the north edge<br>(<code>setY</code> or <code>setConstraint</code>)</td>
  275. * <td><code>height</code> value is automatically set to <code>south - y</code>.</td>
  276. * </tr>
  277. * <tr>
  278. * <td><code>height</code><br>(<code>setHeight</code>)</td>
  279. * <td>south edge's value is automatically set to <code>y + height</code>.</td>
  280. * </tr>
  281. * <tr>
  282. * <td>south edge<br>(<code>setConstraint</code>)</td>
  283. * <td><code>y</code> value is automatically set to <code>south - height</code>.</td>
  284. * </tr>
  285. * </table>
  286. *
  287. */
  288. public static class Constraints {
  289. private Spring x;
  290. private Spring y;
  291. private Spring width;
  292. private Spring height;
  293. private Spring east;
  294. private Spring south;
  295. private Spring verticalDerived = null;
  296. private Spring horizontalDerived = null;
  297. /**
  298. * Creates an empty <code>Constraints</code> object.
  299. */
  300. public Constraints() {
  301. this(null, null, null, null);
  302. }
  303. /**
  304. * Creates a <code>Constraints</code> object with the
  305. * specified values for its
  306. * <code>x</code> and <code>y</code> properties.
  307. * The <code>height</code> and <code>width</code> springs
  308. * have <code>null</code> values.
  309. *
  310. * @param x the spring controlling the component's <em>x</em> value
  311. * @param y the spring controlling the component's <em>y</em> value
  312. */
  313. public Constraints(Spring x, Spring y) {
  314. this(x, y, null, null);
  315. }
  316. /**
  317. * Creates a <code>Constraints</code> object with the
  318. * specified values for its
  319. * <code>x</code>, <code>y</code>, <code>width</code>,
  320. * and <code>height</code> properties.
  321. * Note: If the <code>SpringLayout</code> class
  322. * encounters <code>null</code> values in the
  323. * <code>Constraints</code> object of a given component,
  324. * it replaces them with suitable defaults.
  325. *
  326. * @param x the spring value for the <code>x</code> property
  327. * @param y the spring value for the <code>y</code> property
  328. * @param width the spring value for the <code>width</code> property
  329. * @param height the spring value for the <code>height</code> property
  330. */
  331. public Constraints(Spring x, Spring y, Spring width, Spring height) {
  332. this.x = x;
  333. this.y = y;
  334. this.width = width;
  335. this.height = height;
  336. }
  337. /**
  338. * Creates a <code>Constraints</code> object with
  339. * suitable <code>x</code>, <code>y</code>, <code>width</code> and
  340. * <code>height</code> springs for component, <code>c</code>.
  341. * The <code>x</code> and <code>y</code> springs are constant
  342. * springs initialised with the component's location at
  343. * the time this method is called. The <code>width</code> and
  344. * <code>height</code> springs are special springs, created by
  345. * the <code>Spring.width()</code> and <code>Spring.height()</code>
  346. * methods, which track the size characteristics of the component
  347. * when they change.
  348. *
  349. * @param c the component whose characteristics will be reflected by this Constraints object
  350. * @throws NullPointerException if <code>c</code> is null.
  351. * @since 1.5
  352. */
  353. public Constraints(Component c) {
  354. this.x = Spring.constant(c.getX());
  355. this.y = Spring.constant(c.getY());
  356. this.width = Spring.width(c);
  357. this.height = Spring.height(c);
  358. }
  359. private boolean overConstrainedHorizontally() {
  360. return (x != null) && (width != null) && (east != null);
  361. }
  362. private boolean overConstrainedVertically() {
  363. return (y != null) && (height != null) && (south != null);
  364. }
  365. private Spring sum(Spring s1, Spring s2) {
  366. return (s1 == null || s2 == null) ? null : Spring.sum(s1, s2);
  367. }
  368. private Spring difference(Spring s1, Spring s2) {
  369. return (s1 == null || s2 == null) ? null : Spring.difference(s1, s2);
  370. }
  371. /**
  372. * Sets the <code>x</code> property,
  373. * which controls the <code>x</code> value
  374. * of a component's location.
  375. *
  376. * @param x the spring controlling the <code>x</code> value
  377. * of a component's location
  378. *
  379. * @see #getX
  380. * @see SpringLayout.Constraints
  381. */
  382. public void setX(Spring x) {
  383. this.x = x;
  384. horizontalDerived = null;
  385. if (overConstrainedHorizontally()) {
  386. width = null;
  387. }
  388. }
  389. /**
  390. * Returns the value of the <code>x</code> property.
  391. *
  392. * @return the spring controlling the <code>x</code> value
  393. * of a component's location
  394. *
  395. * @see #setX
  396. * @see SpringLayout.Constraints
  397. */
  398. public Spring getX() {
  399. if (x != null) {
  400. return x;
  401. }
  402. if (horizontalDerived == null) {
  403. horizontalDerived = difference(east, width);
  404. }
  405. return horizontalDerived;
  406. }
  407. /**
  408. * Sets the <code>y</code> property,
  409. * which controls the <code>y</code> value
  410. * of a component's location.
  411. *
  412. * @param y the spring controlling the <code>y</code> value
  413. * of a component's location
  414. *
  415. * @see #getY
  416. * @see SpringLayout.Constraints
  417. */
  418. public void setY(Spring y) {
  419. this.y = y;
  420. verticalDerived = null;
  421. if (overConstrainedVertically()) {
  422. height = null;
  423. }
  424. }
  425. /**
  426. * Returns the value of the <code>y</code> property.
  427. *
  428. * @return the spring controlling the <code>y</code> value
  429. * of a component's location
  430. *
  431. * @see #setY
  432. * @see SpringLayout.Constraints
  433. */
  434. public Spring getY() {
  435. if (y != null) {
  436. return y;
  437. }
  438. if (verticalDerived == null) {
  439. verticalDerived = difference(south, height);
  440. }
  441. return verticalDerived;
  442. }
  443. /**
  444. * Sets the <code>width</code> property,
  445. * which controls the width of a component.
  446. *
  447. * @param width the spring controlling the width of this
  448. * <code>Constraints</code> object
  449. *
  450. * @see #getWidth
  451. * @see SpringLayout.Constraints
  452. */
  453. public void setWidth(Spring width) {
  454. this.width = width;
  455. horizontalDerived = null;
  456. if (overConstrainedHorizontally()) {
  457. east = null;
  458. }
  459. }
  460. /**
  461. * Returns the value of the <code>width</code> property.
  462. *
  463. * @return the spring controlling the width of a component
  464. *
  465. * @see #setWidth
  466. * @see SpringLayout.Constraints
  467. */
  468. public Spring getWidth() {
  469. if (width != null) {
  470. return width;
  471. }
  472. if (horizontalDerived == null) {
  473. horizontalDerived = difference(east, x);
  474. }
  475. return horizontalDerived;
  476. }
  477. /**
  478. * Sets the <code>height</code> property,
  479. * which controls the height of a component.
  480. *
  481. * @param height the spring controlling the height of this <code>Constraints</code>
  482. * object
  483. *
  484. * @see #getHeight
  485. * @see SpringLayout.Constraints
  486. */
  487. public void setHeight(Spring height) {
  488. this.height = height;
  489. verticalDerived = null;
  490. if (overConstrainedVertically()) {
  491. south = null;
  492. }
  493. }
  494. /**
  495. * Returns the value of the <code>height</code> property.
  496. *
  497. * @return the spring controlling the height of a component
  498. *
  499. * @see #setHeight
  500. * @see SpringLayout.Constraints
  501. */
  502. public Spring getHeight() {
  503. if (height != null) {
  504. return height;
  505. }
  506. if (verticalDerived == null) {
  507. verticalDerived = difference(south, y);
  508. }
  509. return verticalDerived;
  510. }
  511. private void setEast(Spring east) {
  512. this.east = east;
  513. horizontalDerived = null;
  514. if (overConstrainedHorizontally()) {
  515. x = null;
  516. }
  517. }
  518. private Spring getEast() {
  519. if (east != null) {
  520. return east;
  521. }
  522. if (horizontalDerived == null) {
  523. horizontalDerived = sum(x, width);
  524. }
  525. return horizontalDerived;
  526. }
  527. private void setSouth(Spring south) {
  528. this.south = south;
  529. verticalDerived = null;
  530. if (overConstrainedVertically()) {
  531. y = null;
  532. }
  533. }
  534. private Spring getSouth() {
  535. if (south != null) {
  536. return south;
  537. }
  538. if (verticalDerived == null) {
  539. verticalDerived = sum(y, height);
  540. }
  541. return verticalDerived;
  542. }
  543. /**
  544. * Sets the spring controlling the specified edge.
  545. * The edge must have one of the following values:
  546. * <code>SpringLayout.NORTH</code>, <code>SpringLayout.SOUTH</code>,
  547. * <code>SpringLayout.EAST</code>, <code>SpringLayout.WEST</code>.
  548. *
  549. * @param edgeName the edge to be set
  550. * @param s the spring controlling the specified edge
  551. *
  552. * @see #getConstraint
  553. * @see #NORTH
  554. * @see #SOUTH
  555. * @see #EAST
  556. * @see #WEST
  557. * @see SpringLayout.Constraints
  558. */
  559. public void setConstraint(String edgeName, Spring s) {
  560. edgeName = edgeName.intern();
  561. if (edgeName == "West") {
  562. setX(s);
  563. }
  564. else if (edgeName == "North") {
  565. setY(s);
  566. }
  567. else if (edgeName == "East") {
  568. setEast(s);
  569. }
  570. else if (edgeName == "South") {
  571. setSouth(s);
  572. }
  573. }
  574. /**
  575. * Returns the value of the specified edge.
  576. * The edge must have one of the following values:
  577. * <code>SpringLayout.NORTH</code>, <code>SpringLayout.SOUTH</code>,
  578. * <code>SpringLayout.EAST</code>, <code>SpringLayout.WEST</code>.
  579. *
  580. * @param edgeName the edge whose value
  581. * is to be returned
  582. *
  583. * @return the spring controlling the specified edge
  584. *
  585. * @see #setConstraint
  586. * @see #NORTH
  587. * @see #SOUTH
  588. * @see #EAST
  589. * @see #WEST
  590. * @see SpringLayout.Constraints
  591. */
  592. public Spring getConstraint(String edgeName) {
  593. edgeName = edgeName.intern();
  594. return (edgeName == "West") ? getX() :
  595. (edgeName == "North") ? getY() :
  596. (edgeName == "East") ? getEast() :
  597. (edgeName == "South") ? getSouth() :
  598. null;
  599. }
  600. /*pp*/ void reset() {
  601. if (x != null) x.setValue(Spring.UNSET);
  602. if (y != null) y.setValue(Spring.UNSET);
  603. if (width != null) width.setValue(Spring.UNSET);
  604. if (height != null) height.setValue(Spring.UNSET);
  605. if (east != null) east.setValue(Spring.UNSET);
  606. if (south != null) south.setValue(Spring.UNSET);
  607. if (horizontalDerived != null) horizontalDerived.setValue(Spring.UNSET);
  608. if (verticalDerived != null) verticalDerived.setValue(Spring.UNSET);
  609. }
  610. }
  611. private static class SpringProxy extends Spring {
  612. private String edgeName;
  613. private Component c;
  614. private SpringLayout l;
  615. public SpringProxy(String edgeName, Component c, SpringLayout l) {
  616. this.edgeName = edgeName;
  617. this.c = c;
  618. this.l = l;
  619. }
  620. private Spring getConstraint() {
  621. return l.getConstraints(c).getConstraint(edgeName);
  622. }
  623. public int getMinimumValue() {
  624. return getConstraint().getMinimumValue();
  625. }
  626. public int getPreferredValue() {
  627. return getConstraint().getPreferredValue();
  628. }
  629. public int getMaximumValue() {
  630. return getConstraint().getMaximumValue();
  631. }
  632. public int getValue() {
  633. return getConstraint().getValue();
  634. }
  635. public void setValue(int size) {
  636. getConstraint().setValue(size);
  637. }
  638. /*pp*/ boolean isCyclic(SpringLayout l) {
  639. return l.isCyclic(getConstraint());
  640. }
  641. public String toString() {
  642. return "SpringProxy for " + edgeName + " edge of " + c.getName() + ".";
  643. }
  644. }
  645. /**
  646. * Constructs a new <code>SpringLayout</code>.
  647. */
  648. public SpringLayout() {}
  649. private void resetCyclicStatuses() {
  650. cyclicSprings = new HashSet();
  651. acyclicSprings = new HashSet();
  652. }
  653. private void setParent(Container p) {
  654. resetCyclicStatuses();
  655. Constraints pc = getConstraints(p);
  656. pc.setX(Spring.constant(0));
  657. pc.setY(Spring.constant(0));
  658. // The applyDefaults() method automatically adds width and
  659. // height springs that delegate their calculations to the
  660. // getMinimumSize(), getPreferredSize() and getMaximumSize()
  661. // methods of the relevant component. In the case of the
  662. // parent this will cause an infinite loop since these
  663. // methods, in turn, delegate their calculations to the
  664. // layout manager. Check for this case and replace the
  665. // the springs that would cause this problem with a
  666. // constant springs that supply default values.
  667. Spring width = pc.getWidth();
  668. if (width instanceof Spring.WidthSpring && ((Spring.WidthSpring)width).c == p) {
  669. pc.setWidth(Spring.constant(0, 0, Integer.MAX_VALUE));
  670. }
  671. Spring height = pc.getHeight();
  672. if (height instanceof Spring.HeightSpring && ((Spring.HeightSpring)height).c == p) {
  673. pc.setHeight(Spring.constant(0, 0, Integer.MAX_VALUE));
  674. }
  675. }
  676. /*pp*/ boolean isCyclic(Spring s) {
  677. if (s == null) {
  678. return false;
  679. }
  680. if (cyclicSprings.contains(s)) {
  681. return true;
  682. }
  683. if (acyclicSprings.contains(s)) {
  684. return false;
  685. }
  686. cyclicSprings.add(s);
  687. boolean result = s.isCyclic(this);
  688. if (!result) {
  689. acyclicSprings.add(s);
  690. cyclicSprings.remove(s);
  691. }
  692. else {
  693. System.err.println(s + " is cyclic. ");
  694. }
  695. return result;
  696. }
  697. private Spring abandonCycles(Spring s) {
  698. return isCyclic(s) ? cyclicReference : s;
  699. }
  700. // LayoutManager methods.
  701. /**
  702. * Has no effect,
  703. * since this layout manager does not
  704. * use a per-component string.
  705. */
  706. public void addLayoutComponent(String name, Component c) {}
  707. /**
  708. * Removes the constraints associated with the specified component.
  709. *
  710. * @param c the component being removed from the container
  711. */
  712. public void removeLayoutComponent(Component c) {
  713. componentConstraints.remove(c);
  714. }
  715. private static Dimension addInsets(int width, int height, Container p) {
  716. Insets i = p.getInsets();
  717. return new Dimension(width + i.left + i.right, height + i.top + i.bottom);
  718. }
  719. public Dimension minimumLayoutSize(Container parent) {
  720. setParent(parent);
  721. Constraints pc = getConstraints(parent);
  722. return addInsets(abandonCycles(pc.getWidth()).getMinimumValue(),
  723. abandonCycles(pc.getHeight()).getMinimumValue(),
  724. parent);
  725. }
  726. public Dimension preferredLayoutSize(Container parent) {
  727. setParent(parent);
  728. Constraints pc = getConstraints(parent);
  729. return addInsets(abandonCycles(pc.getWidth()).getPreferredValue(),
  730. abandonCycles(pc.getHeight()).getPreferredValue(),
  731. parent);
  732. }
  733. // LayoutManager2 methods.
  734. public Dimension maximumLayoutSize(Container parent) {
  735. setParent(parent);
  736. Constraints pc = getConstraints(parent);
  737. return addInsets(abandonCycles(pc.getWidth()).getMaximumValue(),
  738. abandonCycles(pc.getHeight()).getMaximumValue(),
  739. parent);
  740. }
  741. /**
  742. * If <code>constraints</code> is an instance of
  743. * <code>SpringLayout.Constraints</code>,
  744. * associates the constraints with the specified component.
  745. * <p>
  746. * @param component the component being added
  747. * @param constraints the component's constraints
  748. *
  749. * @see SpringLayout.Constraints
  750. */
  751. public void addLayoutComponent(Component component, Object constraints) {
  752. if (constraints instanceof Constraints) {
  753. putConstraints(component, (Constraints)constraints);
  754. }
  755. }
  756. /**
  757. * Returns 0.5f (centered).
  758. */
  759. public float getLayoutAlignmentX(Container p) {
  760. return 0.5f;
  761. }
  762. /**
  763. * Returns 0.5f (centered).
  764. */
  765. public float getLayoutAlignmentY(Container p) {
  766. return 0.5f;
  767. }
  768. public void invalidateLayout(Container p) {}
  769. // End of LayoutManger2 methods
  770. /**
  771. * Links edge <code>e1</code> of component <code>c1</code> to
  772. * edge <code>e2</code> of component <code>c2</code>,
  773. * with a fixed distance between the edges. This
  774. * constraint will cause the assignment
  775. * <pre>
  776. * value(e1, c1) = value(e2, c2) + pad</pre>
  777. * to take place during all subsequent layout operations.
  778. * <p>
  779. * @param e1 the edge of the dependent
  780. * @param c1 the component of the dependent
  781. * @param pad the fixed distance between dependent and anchor
  782. * @param e2 the edge of the anchor
  783. * @param c2 the component of the anchor
  784. *
  785. * @see #putConstraint(String, Component, Spring, String, Component)
  786. */
  787. public void putConstraint(String e1, Component c1, int pad, String e2, Component c2) {
  788. putConstraint(e1, c1, Spring.constant(pad), e2, c2);
  789. }
  790. /**
  791. * Links edge <code>e1</code> of component <code>c1</code> to
  792. * edge <code>e2</code> of component <code>c2</code>. As edge
  793. * <code>(e2, c2)</code> changes value, edge <code>(e1, c1)</code> will
  794. * be calculated by taking the (spring) sum of <code>(e2, c2)</code>
  795. * and <code>s</code>. Each edge must have one of the following values:
  796. * <code>SpringLayout.NORTH</code>, <code>SpringLayout.SOUTH</code>,
  797. * <code>SpringLayout.EAST</code>, <code>SpringLayout.WEST</code>.
  798. * <p>
  799. * @param e1 the edge of the dependent
  800. * @param c1 the component of the dependent
  801. * @param s the spring linking dependent and anchor
  802. * @param e2 the edge of the anchor
  803. * @param c2 the component of the anchor
  804. *
  805. * @see #putConstraint(String, Component, int, String, Component)
  806. * @see #NORTH
  807. * @see #SOUTH
  808. * @see #EAST
  809. * @see #WEST
  810. */
  811. public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2) {
  812. putConstraint(e1, c1, Spring.sum(s, getConstraint(e2, c2)));
  813. }
  814. private void putConstraint(String e, Component c, Spring s) {
  815. if (s != null) {
  816. getConstraints(c).setConstraint(e, s);
  817. }
  818. }
  819. private Constraints applyDefaults(Component c, Constraints constraints) {
  820. if (constraints == null) {
  821. constraints = new Constraints();
  822. }
  823. if (constraints.getWidth() == null) {
  824. constraints.setWidth(new Spring.WidthSpring(c));
  825. }
  826. if (constraints.getHeight() == null) {
  827. constraints.setHeight(new Spring.HeightSpring(c));
  828. }
  829. if (constraints.getX() == null) {
  830. constraints.setX(Spring.constant(0));
  831. }
  832. if (constraints.getY() == null) {
  833. constraints.setY(Spring.constant(0));
  834. }
  835. return constraints;
  836. }
  837. private void putConstraints(Component component, Constraints constraints) {
  838. componentConstraints.put(component, applyDefaults(component, constraints));
  839. }
  840. /**
  841. * Returns the constraints for the specified component.
  842. * Note that,
  843. * unlike the <code>GridBagLayout</code>
  844. * <code>getConstraints</code> method,
  845. * this method does not clone constraints.
  846. * If no constraints
  847. * have been associated with this component,
  848. * this method
  849. * returns a default constraints object positioned at
  850. * 0,0 relative to the parent's Insets and its width/height
  851. * constrained to the minimum, maximum, and preferred sizes of the
  852. * component. The size characteristics
  853. * are not frozen at the time this method is called;
  854. * instead this method returns a constraints object
  855. * whose characteristics track the characteristics
  856. * of the component as they change.
  857. *
  858. * @param c the component whose constraints will be returned
  859. *
  860. * @return the constraints for the specified component
  861. */
  862. public Constraints getConstraints(Component c) {
  863. Constraints result = (Constraints)componentConstraints.get(c);
  864. if (result == null) {
  865. if (c instanceof javax.swing.JComponent) {
  866. Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class);
  867. if (cp instanceof Constraints) {
  868. return applyDefaults(c, (Constraints)cp);
  869. }
  870. }
  871. result = new Constraints();
  872. putConstraints(c, result);
  873. }
  874. return result;
  875. }
  876. /**
  877. * Returns the spring controlling the distance between
  878. * the specified edge of
  879. * the component and the top or left edge of its parent. This
  880. * method, instead of returning the current binding for the
  881. * edge, returns a proxy that tracks the characteristics
  882. * of the edge even if the edge is subsequently rebound.
  883. * Proxies are intended to be used in builder envonments
  884. * where it is useful to allow the user to define the
  885. * constraints for a layout in any order. Proxies do, however,
  886. * provide the means to create cyclic dependencies amongst
  887. * the constraints of a layout. Such cycles are detected
  888. * internally by <code>SpringLayout</code> so that
  889. * the layout operation always terminates.
  890. *
  891. * @param edgeName must be
  892. * <code>SpringLayout.NORTH</code>,
  893. * <code>SpringLayout.SOUTH</code>,
  894. * <code>SpringLayout.EAST</code>, or
  895. * <code>SpringLayout.WEST</code>
  896. * @param c the component whose edge spring is desired
  897. *
  898. * @return a proxy for the spring controlling the distance between the
  899. * specified edge and the top or left edge of its parent
  900. *
  901. * @see #NORTH
  902. * @see #SOUTH
  903. * @see #EAST
  904. * @see #WEST
  905. */
  906. public Spring getConstraint(String edgeName, Component c) {
  907. // The interning here is unnecessary; it was added for efficiency.
  908. edgeName = edgeName.intern();
  909. return new SpringProxy(edgeName, c, this);
  910. }
  911. public void layoutContainer(Container parent) {
  912. setParent(parent);
  913. int n = parent.getComponentCount();
  914. getConstraints(parent).reset();
  915. for (int i = 0 ; i < n ; i++) {
  916. getConstraints(parent.getComponent(i)).reset();
  917. }
  918. Insets insets = parent.getInsets();
  919. Constraints pc = getConstraints(parent);
  920. abandonCycles(pc.getX()).setValue(0);
  921. abandonCycles(pc.getY()).setValue(0);
  922. abandonCycles(pc.getWidth()).setValue(parent.getWidth() -
  923. insets.left - insets.right);
  924. abandonCycles(pc.getHeight()).setValue(parent.getHeight() -
  925. insets.top - insets.bottom);
  926. for (int i = 0 ; i < n ; i++) {
  927. Component c = parent.getComponent(i);
  928. Constraints cc = getConstraints(c);
  929. int x = abandonCycles(cc.getX()).getValue();
  930. int y = abandonCycles(cc.getY()).getValue();
  931. int width = abandonCycles(cc.getWidth()).getValue();
  932. int height = abandonCycles(cc.getHeight()).getValue();
  933. c.setBounds(insets.left + x, insets.top + y, width, height);
  934. }
  935. }
  936. }