1. /*
  2. * @(#)SpringLayout.java 1.15 03/01/23
  3. *
  4. * Copyright 2003 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. *
  144. * @see Spring
  145. * @see SpringLayout.Constraints
  146. *
  147. * @version 1.15 01/23/03
  148. * @author Philip Milne
  149. * @author Joe Winchester
  150. * @since 1.4
  151. */
  152. public class SpringLayout implements LayoutManager2 {
  153. private Map componentConstraints = new HashMap();
  154. private Spring cyclicReference = Spring.constant(Spring.UNSET);
  155. private Set cyclicSprings;
  156. private Set acyclicSprings;
  157. /**
  158. * Specifies the top edge of a component's bounding rectangle.
  159. */
  160. public static final String NORTH = "North";
  161. /**
  162. * Specifies the bottom edge of a component's bounding rectangle.
  163. */
  164. public static final String SOUTH = "South";
  165. /**
  166. * Specifies the right edge of a component's bounding rectangle.
  167. */
  168. public static final String EAST = "East";
  169. /**
  170. * Specifies the left edge of a component's bounding rectangle.
  171. */
  172. public static final String WEST = "West";
  173. private static class WidthSpring extends Spring.AbstractSpring {
  174. private Component c;
  175. public WidthSpring(Component c) {
  176. this.c = c;
  177. }
  178. public int getMinimumValue() {
  179. return c.getMinimumSize().width;
  180. }
  181. public int getPreferredValue() {
  182. return c.getPreferredSize().width;
  183. }
  184. public int getMaximumValue() {
  185. // We will be doing arithmetic with the results of this call,
  186. // so if a returned value is Integer.MAX_VALUE we will get
  187. // arithmetic overflow. Truncate such values.
  188. return Math.min(Short.MAX_VALUE, c.getMaximumSize().width);
  189. }
  190. }
  191. private static class HeightSpring extends Spring.AbstractSpring {
  192. private Component c;
  193. public HeightSpring(Component c) {
  194. this.c = c;
  195. }
  196. public int getMinimumValue() {
  197. return c.getMinimumSize().height;
  198. }
  199. public int getPreferredValue() {
  200. return c.getPreferredSize().height;
  201. }
  202. public int getMaximumValue() {
  203. return Math.min(Short.MAX_VALUE, c.getMaximumSize().height);
  204. }
  205. }
  206. /**
  207. * A <code>Constraints</code> object holds the
  208. * constraints that govern the way a component's size and position
  209. * change in a container controlled by a <code>SpringLayout</code>.
  210. * A <code>Constraints</code> object is
  211. * like a <code>Rectangle</code>, in that it
  212. * has <code>x</code>, <code>y</code>,
  213. * <code>width</code>, and <code>height</code> properties.
  214. * In the <code>Constraints</code> object, however,
  215. * these properties have
  216. * <code>Spring</code> values instead of integers.
  217. * In addition,
  218. * a <code>Constraints</code> object
  219. * can be manipulated as four edges
  220. * -- north, south, east, and west --
  221. * using the <code>constraint</code> property.
  222. *
  223. * <p>
  224. * The following formulas are always true
  225. * for a <code>Constraints</code> object:
  226. *
  227. * <pre>
  228. * west = x
  229. * north = y
  230. * east = x + width
  231. * south = y + height</pre>
  232. *
  233. * <b>Note</b>: In this document,
  234. * operators represent methods
  235. * in the <code>Spring</code> class.
  236. * For example, "a + b" is equal to
  237. * <code>Spring.sum(a, b)</code>,
  238. * and "a - b" is equal to
  239. * <code>Spring.sum(a, Spring.minus(b))</code>.
  240. * See the
  241. * {@link Spring Spring</code> API documentation<code>}
  242. * for further details
  243. * of spring arithmetic.
  244. *
  245. * <p>
  246. *
  247. * Because a <code>Constraints</code> object's properties --
  248. * representing its edges, size, and location -- can all be set
  249. * independently and yet are interrelated,
  250. * the object can become <em>over-constrained</em>.
  251. * For example,
  252. * if both the <code>x</code> and <code>width</code>
  253. * properties are set
  254. * and then the east edge is set,
  255. * the object is over-constrained horizontally.
  256. * When this happens, one of the values
  257. * (in this case, the <code>x</code> property)
  258. * automatically changes so
  259. * that the formulas still hold.
  260. *
  261. * <p>
  262. * The following table shows which value changes
  263. * when a <code>Constraints</code> object
  264. * is over-constrained horizontally.
  265. *
  266. * <p>
  267. *
  268. * <table border=1 summary="Shows which value changes when a Constraints object is over-constrained horizontally">
  269. * <tr>
  270. * <th valign=top>Value Being Set<br>(method used)</th>
  271. * <th valign=top>Result When Over-Constrained Horizontally<br>
  272. * (<code>x</code>, <code>width</code>, and the east edge are all non-<code>null</code>)</th>
  273. * </tr>
  274. * <tr>
  275. * <td><code>x</code> or the west edge <br>(<code>setX</code> or <code>setConstraint</code>)</td>
  276. * <td><code>width</code> value is automatically set to <code>east - x</code>.</td>
  277. * </tr>
  278. * <tr>
  279. * <td><code>width</code><br>(<code>setWidth</code>)</td>
  280. * <td>east edge's value is automatically set to <code>x + width</code>.</td>
  281. * </tr>
  282. * <tr>
  283. * <td>east edge<br>(<code>setConstraint</code>)</td>
  284. * <td><code>x</code> value is automatically set to <code>east - width</code>.</td>
  285. * </tr>
  286. * </table>
  287. *
  288. * <p>
  289. * The rules for the vertical properties are similar:
  290. * <p>
  291. *
  292. * <table border=1 summary="Shows which value changes when a Constraints object is over-constrained vertically">
  293. * <tr>
  294. * <th valign=top>Value Being Set<br>(method used)</th>
  295. * <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>
  296. * </tr>
  297. * <tr>
  298. * <td><code>y</code> or the north edge<br>(<code>setY</code> or <code>setConstraint</code>)</td>
  299. * <td><code>height</code> value is automatically set to <code>south - y</code>.</td>
  300. * </tr>
  301. * <tr>
  302. * <td><code>height</code><br>(<code>setHeight</code>)</td>
  303. * <td>south edge's value is automatically set to <code>y + height</code>.</td>
  304. * </tr>
  305. * <tr>
  306. * <td>south edge<br>(<code>setConstraint</code>)</td>
  307. * <td><code>y</code> value is automatically set to <code>south - height</code>.</td>
  308. * </tr>
  309. * </table>
  310. *
  311. */
  312. public static class Constraints {
  313. private Spring x;
  314. private Spring y;
  315. private Spring width;
  316. private Spring height;
  317. private Spring east;
  318. private Spring south;
  319. private Spring verticalDerived = null;
  320. private Spring horizontalDerived = null;
  321. /**
  322. * Creates an empty <code>Constraints</code> object.
  323. */
  324. public Constraints() {
  325. this(null, null, null, null);
  326. }
  327. /**
  328. * Creates a <code>Constraints</code> object with the
  329. * specified values for its
  330. * <code>x</code> and <code>y</code> properties.
  331. * The <code>height</code> and <code>width</code> springs
  332. * have <code>null</code> values.
  333. *
  334. * @param x the spring controlling the component's <em>x</em> value
  335. * @param y the spring controlling the component's <em>y</em> value
  336. */
  337. public Constraints(Spring x, Spring y) {
  338. this(x, y, null, null);
  339. }
  340. /**
  341. * Creates a <code>Constraints</code> object with the
  342. * specified values for its
  343. * <code>x</code>, <code>y</code>, <code>width</code>,
  344. * and <code>height</code> properties.
  345. * Note: If the <code>SpringLayout</code> class
  346. * encounters <code>null</code> values in the
  347. * <code>Constraints</code> object of a given component,
  348. * it replaces them with suitable defaults.
  349. *
  350. * @param x the spring value for the <code>x</code> property
  351. * @param y the spring value for the <code>y</code> property
  352. * @param width the spring value for the <code>width</code> property
  353. * @param height the spring value for the <code>height</code> property
  354. */
  355. public Constraints(Spring x, Spring y, Spring width, Spring height) {
  356. this.x = x;
  357. this.y = y;
  358. this.width = width;
  359. this.height = height;
  360. }
  361. private boolean overConstrainedHorizontally() {
  362. return (x != null) && (width != null) && (east != null);
  363. }
  364. private boolean overConstrainedVertically() {
  365. return (y != null) && (height != null) && (south != null);
  366. }
  367. private Spring sum(Spring s1, Spring s2) {
  368. return (s1 == null || s2 == null) ? null : Spring.sum(s1, s2);
  369. }
  370. private Spring difference(Spring s1, Spring s2) {
  371. return (s1 == null || s2 == null) ? null : Spring.difference(s1, s2);
  372. }
  373. /**
  374. * Sets the <code>x</code> property,
  375. * which controls the <code>x</code> value
  376. * of a component's location.
  377. *
  378. * @param x the spring controlling the <code>x</code> value
  379. * of a component's location
  380. *
  381. * @see #getX
  382. * @see SpringLayout.Constraints
  383. */
  384. public void setX(Spring x) {
  385. this.x = x;
  386. horizontalDerived = null;
  387. if (overConstrainedHorizontally()) {
  388. width = null;
  389. }
  390. }
  391. /**
  392. * Returns the value of the <code>x</code> property.
  393. *
  394. * @return the spring controlling the <code>x</code> value
  395. * of a component's location
  396. *
  397. * @see #setX
  398. * @see SpringLayout.Constraints
  399. */
  400. public Spring getX() {
  401. if (x != null) {
  402. return x;
  403. }
  404. if (horizontalDerived == null) {
  405. horizontalDerived = difference(east, width);
  406. }
  407. return horizontalDerived;
  408. }
  409. /**
  410. * Sets the <code>y</code> property,
  411. * which controls the <code>y</code> value
  412. * of a component's location.
  413. *
  414. * @param y the spring controlling the <code>y</code> value
  415. * of a component's location
  416. *
  417. * @see #getY
  418. * @see SpringLayout.Constraints
  419. */
  420. public void setY(Spring y) {
  421. this.y = y;
  422. verticalDerived = null;
  423. if (overConstrainedVertically()) {
  424. height = null;
  425. }
  426. }
  427. /**
  428. * Returns the value of the <code>y</code> property.
  429. *
  430. * @return the spring controlling the <code>y</code> value
  431. * of a component's location
  432. *
  433. * @see #setY
  434. * @see SpringLayout.Constraints
  435. */
  436. public Spring getY() {
  437. if (y != null) {
  438. return y;
  439. }
  440. if (verticalDerived == null) {
  441. verticalDerived = difference(south, height);
  442. }
  443. return verticalDerived;
  444. }
  445. /**
  446. * Sets the <code>width</code> property,
  447. * which controls the width of a component.
  448. *
  449. * @param width the spring controlling the width of this
  450. * <code>Constraints</code> object
  451. *
  452. * @see #getWidth
  453. * @see SpringLayout.Constraints
  454. */
  455. public void setWidth(Spring width) {
  456. this.width = width;
  457. horizontalDerived = null;
  458. if (overConstrainedHorizontally()) {
  459. east = null;
  460. }
  461. }
  462. /**
  463. * Returns the value of the <code>width</code> property.
  464. *
  465. * @return the spring controlling the width of a component
  466. *
  467. * @see #setWidth
  468. * @see SpringLayout.Constraints
  469. */
  470. public Spring getWidth() {
  471. if (width != null) {
  472. return width;
  473. }
  474. if (horizontalDerived == null) {
  475. horizontalDerived = difference(east, x);
  476. }
  477. return horizontalDerived;
  478. }
  479. /**
  480. * Sets the <code>height</code> property,
  481. * which controls the height of a component.
  482. *
  483. * @param height the spring controlling the height of this <code>Constraints</code>
  484. * object
  485. *
  486. * @see #getHeight
  487. * @see SpringLayout.Constraints
  488. */
  489. public void setHeight(Spring height) {
  490. this.height = height;
  491. verticalDerived = null;
  492. if (overConstrainedVertically()) {
  493. south = null;
  494. }
  495. }
  496. /**
  497. * Returns the value of the <code>height</code> property.
  498. *
  499. * @return the spring controlling the height of a component
  500. *
  501. * @see #setHeight
  502. * @see SpringLayout.Constraints
  503. */
  504. public Spring getHeight() {
  505. if (height != null) {
  506. return height;
  507. }
  508. if (verticalDerived == null) {
  509. verticalDerived = difference(south, y);
  510. }
  511. return verticalDerived;
  512. }
  513. private void setEast(Spring east) {
  514. this.east = east;
  515. horizontalDerived = null;
  516. if (overConstrainedHorizontally()) {
  517. x = null;
  518. }
  519. }
  520. private Spring getEast() {
  521. if (east != null) {
  522. return east;
  523. }
  524. if (horizontalDerived == null) {
  525. horizontalDerived = sum(x, width);
  526. }
  527. return horizontalDerived;
  528. }
  529. private void setSouth(Spring south) {
  530. this.south = south;
  531. verticalDerived = null;
  532. if (overConstrainedVertically()) {
  533. y = null;
  534. }
  535. }
  536. private Spring getSouth() {
  537. if (south != null) {
  538. return south;
  539. }
  540. if (verticalDerived == null) {
  541. verticalDerived = sum(y, height);
  542. }
  543. return verticalDerived;
  544. }
  545. /**
  546. * Sets the spring controlling the specified edge.
  547. * The edge must have one of the following values:
  548. * <code>SpringLayout.NORTH</code>, <code>SpringLayout.SOUTH</code>,
  549. * <code>SpringLayout.EAST</code>, <code>SpringLayout.WEST</code>.
  550. *
  551. * @param edgeName the edge to be set
  552. * @param s the spring controlling the specified edge
  553. *
  554. * @see #getConstraint
  555. * @see #NORTH
  556. * @see #SOUTH
  557. * @see #EAST
  558. * @see #WEST
  559. * @see SpringLayout.Constraints
  560. */
  561. public void setConstraint(String edgeName, Spring s) {
  562. edgeName = edgeName.intern();
  563. if (edgeName == "West") {
  564. setX(s);
  565. }
  566. else if (edgeName == "North") {
  567. setY(s);
  568. }
  569. else if (edgeName == "East") {
  570. setEast(s);
  571. }
  572. else if (edgeName == "South") {
  573. setSouth(s);
  574. }
  575. }
  576. /**
  577. * Returns the value of the specified edge.
  578. * The edge must have one of the following values:
  579. * <code>SpringLayout.NORTH</code>, <code>SpringLayout.SOUTH</code>,
  580. * <code>SpringLayout.EAST</code>, <code>SpringLayout.WEST</code>.
  581. *
  582. * @param edgeName the edge whose value
  583. * is to be returned
  584. *
  585. * @return the spring controlling the specified edge
  586. *
  587. * @see #setConstraint
  588. * @see #NORTH
  589. * @see #SOUTH
  590. * @see #EAST
  591. * @see #WEST
  592. * @see SpringLayout.Constraints
  593. */
  594. public Spring getConstraint(String edgeName) {
  595. edgeName = edgeName.intern();
  596. return (edgeName == "West") ? getX() :
  597. (edgeName == "North") ? getY() :
  598. (edgeName == "East") ? getEast() :
  599. (edgeName == "South") ? getSouth() :
  600. null;
  601. }
  602. /*pp*/ void reset() {
  603. if (x != null) x.setValue(Spring.UNSET);
  604. if (y != null) y.setValue(Spring.UNSET);
  605. if (width != null) width.setValue(Spring.UNSET);
  606. if (height != null) height.setValue(Spring.UNSET);
  607. if (east != null) east.setValue(Spring.UNSET);
  608. if (south != null) south.setValue(Spring.UNSET);
  609. if (horizontalDerived != null) horizontalDerived.setValue(Spring.UNSET);
  610. if (verticalDerived != null) verticalDerived.setValue(Spring.UNSET);
  611. }
  612. }
  613. private static class SpringProxy extends Spring {
  614. private String edgeName;
  615. private Component c;
  616. private SpringLayout l;
  617. public SpringProxy(String edgeName, Component c, SpringLayout l) {
  618. this.edgeName = edgeName;
  619. this.c = c;
  620. this.l = l;
  621. }
  622. private Spring getConstraint() {
  623. return l.getConstraints(c).getConstraint(edgeName);
  624. }
  625. public int getMinimumValue() {
  626. return getConstraint().getMinimumValue();
  627. }
  628. public int getPreferredValue() {
  629. return getConstraint().getPreferredValue();
  630. }
  631. public int getMaximumValue() {
  632. return getConstraint().getMaximumValue();
  633. }
  634. public int getValue() {
  635. return getConstraint().getValue();
  636. }
  637. public void setValue(int size) {
  638. getConstraint().setValue(size);
  639. }
  640. /*pp*/ boolean isCyclic(SpringLayout l) {
  641. return l.isCyclic(getConstraint());
  642. }
  643. public String toString() {
  644. return "SpringProxy for " + edgeName + " edge of " + c.getName() + ".";
  645. }
  646. }
  647. /**
  648. * Constructs a new <code>SpringLayout</code>.
  649. */
  650. public SpringLayout() {}
  651. private void resetCyclicStatuses() {
  652. cyclicSprings = new HashSet();
  653. acyclicSprings = new HashSet();
  654. }
  655. private void setParent(Container p) {
  656. resetCyclicStatuses();
  657. Constraints pc = getConstraints(p);
  658. pc.setX(Spring.constant(0));
  659. pc.setY(Spring.constant(0));
  660. pc.setWidth(null);
  661. pc.setHeight(null);
  662. if (pc.getEast() == null) {
  663. pc.setEast(Spring.constant(0, 0, Integer.MAX_VALUE));
  664. }
  665. if (pc.getSouth() == null) {
  666. pc.setSouth(Spring.constant(0, 0, Integer.MAX_VALUE));
  667. }
  668. }
  669. /*pp*/ boolean isCyclic(Spring s) {
  670. if (s == null) {
  671. return false;
  672. }
  673. if (cyclicSprings.contains(s)) {
  674. return true;
  675. }
  676. if (acyclicSprings.contains(s)) {
  677. return false;
  678. }
  679. cyclicSprings.add(s);
  680. boolean result = s.isCyclic(this);
  681. if (!result) {
  682. acyclicSprings.add(s);
  683. cyclicSprings.remove(s);
  684. }
  685. else {
  686. System.err.println(s + " is cyclic. ");
  687. }
  688. return result;
  689. }
  690. private Spring abandonCycles(Spring s) {
  691. return isCyclic(s) ? cyclicReference : s;
  692. }
  693. // LayoutManager methods.
  694. /**
  695. * Has no effect,
  696. * since this layout manager does not
  697. * use a per-component string.
  698. */
  699. public void addLayoutComponent(String name, Component c) {}
  700. /**
  701. * Removes the constraints associated with the specified component.
  702. *
  703. * @param c the component being removed from the container
  704. */
  705. public void removeLayoutComponent(Component c) {
  706. componentConstraints.remove(c);
  707. }
  708. private static Dimension addInsets(int width, int height, Container p) {
  709. Insets i = p.getInsets();
  710. return new Dimension(width + i.left + i.right, height + i.top + i.bottom);
  711. }
  712. public Dimension minimumLayoutSize(Container parent) {
  713. setParent(parent);
  714. Constraints pc = getConstraints(parent);
  715. return addInsets(abandonCycles(pc.getWidth()).getMinimumValue(),
  716. abandonCycles(pc.getHeight()).getMinimumValue(),
  717. parent);
  718. }
  719. public Dimension preferredLayoutSize(Container parent) {
  720. setParent(parent);
  721. Constraints pc = getConstraints(parent);
  722. return addInsets(abandonCycles(pc.getWidth()).getPreferredValue(),
  723. abandonCycles(pc.getHeight()).getPreferredValue(),
  724. parent);
  725. }
  726. // LayoutManager2 methods.
  727. public Dimension maximumLayoutSize(Container parent) {
  728. setParent(parent);
  729. Constraints pc = getConstraints(parent);
  730. return addInsets(abandonCycles(pc.getWidth()).getMaximumValue(),
  731. abandonCycles(pc.getHeight()).getMaximumValue(),
  732. parent);
  733. }
  734. /**
  735. * If <code>constraints</code> is an instance of
  736. * <code>SpringLayout.Constraints</code>,
  737. * associates the constraints with the specified component.
  738. * <p>
  739. * @param component the component being added
  740. * @param constraints the component's constraints
  741. *
  742. * @see SpringLayout.Constraints
  743. */
  744. public void addLayoutComponent(Component component, Object constraints) {
  745. if (constraints instanceof Constraints) {
  746. putConstraints(component, (Constraints)constraints);
  747. }
  748. }
  749. /**
  750. * Returns 0.5f (centered).
  751. */
  752. public float getLayoutAlignmentX(Container p) {
  753. return 0.5f;
  754. }
  755. /**
  756. * Returns 0.5f (centered).
  757. */
  758. public float getLayoutAlignmentY(Container p) {
  759. return 0.5f;
  760. }
  761. public void invalidateLayout(Container p) {}
  762. // End of LayoutManger2 methods
  763. /**
  764. * Links edge <code>e1</code> of component <code>c1</code> to
  765. * edge <code>e2</code> of component <code>c2</code>,
  766. * with a fixed distance between the edges. This
  767. * constraint will cause the assignment
  768. * <pre>
  769. * value(e1, c1) = value(e2, c2) + pad</pre>
  770. * to take place during all subsequent layout operations.
  771. * <p>
  772. * @param e1 the edge of the dependent
  773. * @param c1 the component of the dependent
  774. * @param pad the fixed distance between dependent and anchor
  775. * @param e2 the edge of the anchor
  776. * @param c2 the component of the anchor
  777. *
  778. * @see #putConstraint(String, Component, Spring, String, Component)
  779. */
  780. public void putConstraint(String e1, Component c1, int pad, String e2, Component c2) {
  781. putConstraint(e1, c1, Spring.constant(pad), e2, c2);
  782. }
  783. /**
  784. * Links edge <code>e1</code> of component <code>c1</code> to
  785. * edge <code>e2</code> of component <code>c2</code>. As edge
  786. * <code>(e2, c2)</code> changes value, edge <code>(e1, c1)</code> will
  787. * be calculated by taking the (spring) sum of <code>(e2, c2)</code>
  788. * and <code>s</code>. Each edge must have one of the following values:
  789. * <code>SpringLayout.NORTH</code>, <code>SpringLayout.SOUTH</code>,
  790. * <code>SpringLayout.EAST</code>, <code>SpringLayout.WEST</code>.
  791. * <p>
  792. * @param e1 the edge of the dependent
  793. * @param c1 the component of the dependent
  794. * @param s the spring linking dependent and anchor
  795. * @param e2 the edge of the anchor
  796. * @param c2 the component of the anchor
  797. *
  798. * @see #putConstraint(String, Component, int, String, Component)
  799. * @see #NORTH
  800. * @see #SOUTH
  801. * @see #EAST
  802. * @see #WEST
  803. */
  804. public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2) {
  805. putConstraint(e1, c1, Spring.sum(s, getConstraint(e2, c2)));
  806. }
  807. private void putConstraint(String e, Component c, Spring s) {
  808. if (s != null) {
  809. getConstraints(c).setConstraint(e, s);
  810. }
  811. }
  812. private Constraints applyDefaults(Component c, Constraints constraints) {
  813. if (constraints == null) {
  814. constraints = new Constraints();
  815. }
  816. if (constraints.getWidth() == null) {
  817. constraints.setWidth(new WidthSpring(c));
  818. }
  819. if (constraints.getHeight() == null) {
  820. constraints.setHeight(new HeightSpring(c));
  821. }
  822. if (constraints.getX() == null) {
  823. constraints.setX(Spring.constant(0));
  824. }
  825. if (constraints.getY() == null) {
  826. constraints.setY(Spring.constant(0));
  827. }
  828. return constraints;
  829. }
  830. private void putConstraints(Component component, Constraints constraints) {
  831. componentConstraints.put(component, applyDefaults(component, constraints));
  832. }
  833. /**
  834. * Returns the constraints for the specified component.
  835. * Note that,
  836. * unlike the <code>GridBagLayout</code>
  837. * <code>getConstraints</code> method,
  838. * this method does not clone constraints.
  839. * If no constraints
  840. * have been associated with this component,
  841. * this method
  842. * returns a default constraints object positioned at
  843. * 0,0 relative to the parent's Insets and its width/height
  844. * constrained to the minimum, maximum, and preferred sizes of the
  845. * component. The size characteristics
  846. * are not frozen at the time this method is called;
  847. * instead this method returns a constraints object
  848. * whose characteristics track the characteristics
  849. * of the component as they change.
  850. *
  851. * @param c the component whose constraints will be returned
  852. *
  853. * @return the constraints for the specified component
  854. */
  855. public Constraints getConstraints(Component c) {
  856. Constraints result = (Constraints)componentConstraints.get(c);
  857. if (result == null) {
  858. if (c instanceof javax.swing.JComponent) {
  859. Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class);
  860. if (cp instanceof Constraints) {
  861. return applyDefaults(c, (Constraints)cp);
  862. }
  863. }
  864. result = new Constraints();
  865. putConstraints(c, result);
  866. }
  867. return result;
  868. }
  869. /**
  870. * Returns the spring controlling the distance between
  871. * the specified edge of
  872. * the component and the top or left edge of its parent. This
  873. * method, instead of returning the current binding for the
  874. * edge, returns a proxy that tracks the characteristics
  875. * of the edge even if the edge is subsequently rebound.
  876. * Proxies are intended to be used in builder envonments
  877. * where it is useful to allow the user to define the
  878. * constraints for a layout in any order. Proxies do, however,
  879. * provide the means to create cyclic dependencies amongst
  880. * the constraints of a layout. Such cycles are detected
  881. * internally by <code>SpringLayout</code> so that
  882. * the layout operation always terminates.
  883. *
  884. * @param edgeName must be
  885. * <code>SpringLayout.NORTH</code>,
  886. * <code>SpringLayout.SOUTH</code>,
  887. * <code>SpringLayout.EAST</code>, or
  888. * <code>SpringLayout.WEST</code>
  889. * @param c the component whose edge spring is desired
  890. *
  891. * @return a proxy for the spring controlling the distance between the
  892. * specified edge and the top or left edge of its parent
  893. *
  894. * @see #NORTH
  895. * @see #SOUTH
  896. * @see #EAST
  897. * @see #WEST
  898. */
  899. public Spring getConstraint(String edgeName, Component c) {
  900. // The interning here is unnecessary; it was added for efficiency.
  901. edgeName = edgeName.intern();
  902. return new SpringProxy(edgeName, c, this);
  903. }
  904. public void layoutContainer(Container parent) {
  905. setParent(parent);
  906. int n = parent.getComponentCount();
  907. getConstraints(parent).reset();
  908. for (int i = 0 ; i < n ; i++) {
  909. getConstraints(parent.getComponent(i)).reset();
  910. }
  911. Insets insets = parent.getInsets();
  912. Constraints pc = getConstraints(parent);
  913. abandonCycles(pc.getX()).setValue(0);
  914. abandonCycles(pc.getY()).setValue(0);
  915. abandonCycles(pc.getWidth()).setValue(parent.getWidth() -
  916. insets.left - insets.right);
  917. abandonCycles(pc.getHeight()).setValue(parent.getHeight() -
  918. insets.top - insets.bottom);
  919. for (int i = 0 ; i < n ; i++) {
  920. Component c = parent.getComponent(i);
  921. Constraints cc = getConstraints(c);
  922. int x = abandonCycles(cc.getX()).getValue();
  923. int y = abandonCycles(cc.getY()).getValue();
  924. int width = abandonCycles(cc.getWidth()).getValue();
  925. int height = abandonCycles(cc.getHeight()).getValue();
  926. c.setBounds(insets.left + x, insets.top + y, width, height);
  927. }
  928. }
  929. }