1. /*
  2. * @(#)BoxLayout.java 1.34 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.io.Serializable;
  10. import java.io.PrintStream;
  11. /**
  12. * A layout manager that allows multiple components to be laid out either
  13. * vertically or horizontally. The components will not wrap so, for
  14. * example, a vertical arrangement of components will stay vertically
  15. * arranged when the frame is resized.
  16. * <TABLE ALIGN="RIGHT" BORDER="0" SUMMARY="layout">
  17. * <TR>
  18. * <TD ALIGN="CENTER">
  19. * <P ALIGN="CENTER"><IMG SRC="doc-files/BoxLayout-1.gif"
  20. * alt="The following text describes this graphic."
  21. * WIDTH="191" HEIGHT="201" ALIGN="BOTTOM" BORDER="0">
  22. * </TD>
  23. * </TR>
  24. * </TABLE>
  25. * <p>
  26. * Nesting multiple panels with different combinations of horizontal and
  27. * vertical gives an effect similar to GridBagLayout, without the
  28. * complexity. The diagram shows two panels arranged horizontally, each
  29. * of which contains 3 components arranged vertically.
  30. *
  31. * <p> The BoxLayout manager is constructed with an axis parameter that
  32. * specifies the type of layout that will be done. There are four choices:
  33. *
  34. * <blockquote><b><tt>X_AXIS</tt></b> - Components are laid out horizontally
  35. * from left to right.</blockquote>
  36. *
  37. * <blockquote><b><tt>Y_AXIS</tt></b> - Components are laid out vertically
  38. * from top to bottom.</blockquote>
  39. *
  40. * <blockquote><b><tt>LINE_AXIS</tt></b> - Components are laid out the way
  41. * words are laid out in a line, based on the container's
  42. * <tt>ComponentOrientation</tt> property. If the container's
  43. * <tt>ComponentOrientation</tt> is horizontal then components are laid out
  44. * horizontally, otherwise they are laid out vertically. For horizontal
  45. * orientations, if the container's <tt>ComponentOrientation</tt> is left to
  46. * right then components are laid out left to right, otherwise they are laid
  47. * out right to left. For vertical orientations components are always laid out
  48. * from top to bottom.</blockquote>
  49. *
  50. * <blockquote><b><tt>PAGE_AXIS</tt></b> - Components are laid out the way
  51. * text lines are laid out on a page, based on the container's
  52. * <tt>ComponentOrientation</tt> property. If the container's
  53. * <tt>ComponentOrientation</tt> is horizontal then components are laid out
  54. * vertically, otherwise they are laid out horizontally. For horizontal
  55. * orientations, if the container's <tt>ComponentOrientation</tt> is left to
  56. * right then components are laid out left to right, otherwise they are laid
  57. * out right to left.  For vertical orientations components are always
  58. * laid out from top to bottom.</blockquote>
  59. * <p>
  60. * For all directions, components are arranged in the same order as they were
  61. * added to the container.
  62. * <p>
  63. * BoxLayout attempts to arrange components
  64. * at their preferred widths (for horizontal layout)
  65. * or heights (for vertical layout).
  66. * For a horizontal layout,
  67. * if not all the components are the same height,
  68. * BoxLayout attempts to make all the components
  69. * as high as the highest component.
  70. * If that's not possible for a particular component,
  71. * then BoxLayout aligns that component vertically,
  72. * according to the component's Y alignment.
  73. * By default, a component has a Y alignment of 0.5,
  74. * which means that the vertical center of the component
  75. * should have the same Y coordinate as
  76. * the vertical centers of other components with 0.5 Y alignment.
  77. * <p>
  78. * Similarly, for a vertical layout,
  79. * BoxLayout attempts to make all components in the column
  80. * as wide as the widest component.
  81. * If that fails, it aligns them horizontally
  82. * according to their X alignments. For <code>PAGE_AXIS</code> layout,
  83. * horizontal alignment is done based on the leading edge of the component.
  84. * In other words, an X alignment value of 0.0 means the left edge of a
  85. * component if the container's <code>ComponentOrientation</code> is left to
  86. * right and it means the right edge of the component otherwise.
  87. * <p>
  88. * Instead of using BoxLayout directly, many programs use the Box class.
  89. * The Box class is a lightweight container that uses a BoxLayout.
  90. * It also provides handy methods to help you use BoxLayout well.
  91. * Adding components to multiple nested boxes is a powerful way to get
  92. * the arrangement you want.
  93. * <p>
  94. * For further information and examples see
  95. * <a
  96. href="http://java.sun.com/docs/books/tutorial/uiswing/layout/box.html">How to Use BoxLayout</a>,
  97. * a section in <em>The Java Tutorial.</em>
  98. * <p>
  99. * <strong>Warning:</strong>
  100. * Serialized objects of this class will not be compatible with
  101. * future Swing releases. The current serialization support is
  102. * appropriate for short term storage or RMI between applications running
  103. * the same version of Swing. As of 1.4, support for long term storage
  104. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  105. * has been added to the <code>java.beans</code> package.
  106. * Please see {@link java.beans.XMLEncoder}.
  107. *
  108. * @see Box
  109. * @see java.awt.ComponentOrientation
  110. * @see JComponent#getAlignmentX
  111. * @see JComponent#getAlignmentY
  112. *
  113. * @author Timothy Prinzing
  114. * @version 1.34 12/19/03
  115. */
  116. public class BoxLayout implements LayoutManager2, Serializable {
  117. /**
  118. * Specifies that components should be laid out left to right.
  119. */
  120. public static final int X_AXIS = 0;
  121. /**
  122. * Specifies that components should be laid out top to bottom.
  123. */
  124. public static final int Y_AXIS = 1;
  125. /**
  126. * Specifies that components should be laid out in the direction of
  127. * a line of text as determined by the target container's
  128. * <code>ComponentOrientation</code> property.
  129. */
  130. public static final int LINE_AXIS = 2;
  131. /**
  132. * Specifies that components should be laid out in the direction that
  133. * lines flow across a page as determined by the target container's
  134. * <code>ComponentOrientation</code> property.
  135. */
  136. public static final int PAGE_AXIS = 3;
  137. /**
  138. * Creates a layout manager that will lay out components along the
  139. * given axis.
  140. *
  141. * @param target the container that needs to be laid out
  142. * @param axis the axis to lay out components along. Can be one of:
  143. * <code>BoxLayout.X_AXIS</code>,
  144. * <code>BoxLayout.Y_AXIS</code>,
  145. * <code>BoxLayout.LINE_AXIS</code> or
  146. * <code>BoxLayout.PAGE_AXIS</code>
  147. *
  148. * @exception AWTError if the value of <code>axis</code> is invalid
  149. */
  150. public BoxLayout(Container target, int axis) {
  151. if (axis != X_AXIS && axis != Y_AXIS &&
  152. axis != LINE_AXIS && axis != PAGE_AXIS) {
  153. throw new AWTError("Invalid axis");
  154. }
  155. this.axis = axis;
  156. this.target = target;
  157. }
  158. /**
  159. * Constructs a BoxLayout that
  160. * produces debugging messages.
  161. *
  162. * @param target the container that needs to be laid out
  163. * @param axis the axis to lay out components along. Can be one of:
  164. * <code>BoxLayout.X_AXIS</code>,
  165. * <code>BoxLayout.Y_AXIS</code>,
  166. * <code>BoxLayout.LINE_AXIS</code> or
  167. * <code>BoxLayout.PAGE_AXIS</code>
  168. *
  169. * @param dbg the stream to which debugging messages should be sent,
  170. * null if none
  171. */
  172. BoxLayout(Container target, int axis, PrintStream dbg) {
  173. this(target, axis);
  174. this.dbg = dbg;
  175. }
  176. /**
  177. * Indicates that a child has changed its layout related information,
  178. * and thus any cached calculations should be flushed.
  179. * <p>
  180. * This method is called by AWT when the invalidate method is called
  181. * on the Container. Since the invalidate method may be called
  182. * asynchronously to the event thread, this method may be called
  183. * asynchronously.
  184. *
  185. * @param target the affected container
  186. *
  187. * @exception AWTError if the target isn't the container specified to the
  188. * BoxLayout constructor
  189. */
  190. public synchronized void invalidateLayout(Container target) {
  191. checkContainer(target);
  192. xChildren = null;
  193. yChildren = null;
  194. xTotal = null;
  195. yTotal = null;
  196. }
  197. /**
  198. * Not used by this class.
  199. *
  200. * @param name the name of the component
  201. * @param comp the component
  202. */
  203. public void addLayoutComponent(String name, Component comp) {
  204. }
  205. /**
  206. * Not used by this class.
  207. *
  208. * @param comp the component
  209. */
  210. public void removeLayoutComponent(Component comp) {
  211. }
  212. /**
  213. * Not used by this class.
  214. *
  215. * @param comp the component
  216. * @param constraints constraints
  217. */
  218. public void addLayoutComponent(Component comp, Object constraints) {
  219. }
  220. /**
  221. * Returns the preferred dimensions for this layout, given the components
  222. * in the specified target container.
  223. *
  224. * @param target the container that needs to be laid out
  225. * @return the dimensions >= 0 && <= Integer.MAX_VALUE
  226. * @exception AWTError if the target isn't the container specified to the
  227. * BoxLayout constructor
  228. * @see Container
  229. * @see #minimumLayoutSize
  230. * @see #maximumLayoutSize
  231. */
  232. public Dimension preferredLayoutSize(Container target) {
  233. Dimension size;
  234. synchronized(this) {
  235. checkContainer(target);
  236. checkRequests();
  237. size = new Dimension(xTotal.preferred, yTotal.preferred);
  238. }
  239. Insets insets = target.getInsets();
  240. size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
  241. size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
  242. return size;
  243. }
  244. /**
  245. * Returns the minimum dimensions needed to lay out the components
  246. * contained in the specified target container.
  247. *
  248. * @param target the container that needs to be laid out
  249. * @return the dimensions >= 0 && <= Integer.MAX_VALUE
  250. * @exception AWTError if the target isn't the container specified to the
  251. * BoxLayout constructor
  252. * @see #preferredLayoutSize
  253. * @see #maximumLayoutSize
  254. */
  255. public Dimension minimumLayoutSize(Container target) {
  256. Dimension size;
  257. synchronized(this) {
  258. checkContainer(target);
  259. checkRequests();
  260. size = new Dimension(xTotal.minimum, yTotal.minimum);
  261. }
  262. Insets insets = target.getInsets();
  263. size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
  264. size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
  265. return size;
  266. }
  267. /**
  268. * Returns the maximum dimensions the target container can use
  269. * to lay out the components it contains.
  270. *
  271. * @param target the container that needs to be laid out
  272. * @return the dimenions >= 0 && <= Integer.MAX_VALUE
  273. * @exception AWTError if the target isn't the container specified to the
  274. * BoxLayout constructor
  275. * @see #preferredLayoutSize
  276. * @see #minimumLayoutSize
  277. */
  278. public Dimension maximumLayoutSize(Container target) {
  279. Dimension size;
  280. synchronized(this) {
  281. checkContainer(target);
  282. checkRequests();
  283. size = new Dimension(xTotal.maximum, yTotal.maximum);
  284. }
  285. Insets insets = target.getInsets();
  286. size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
  287. size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
  288. return size;
  289. }
  290. /**
  291. * Returns the alignment along the X axis for the container.
  292. * If the box is horizontal, the default
  293. * alignment will be returned. Otherwise, the alignment needed
  294. * to place the children along the X axis will be returned.
  295. *
  296. * @param target the container
  297. * @return the alignment >= 0.0f && <= 1.0f
  298. * @exception AWTError if the target isn't the container specified to the
  299. * BoxLayout constructor
  300. */
  301. public synchronized float getLayoutAlignmentX(Container target) {
  302. checkContainer(target);
  303. checkRequests();
  304. return xTotal.alignment;
  305. }
  306. /**
  307. * Returns the alignment along the Y axis for the container.
  308. * If the box is vertical, the default
  309. * alignment will be returned. Otherwise, the alignment needed
  310. * to place the children along the Y axis will be returned.
  311. *
  312. * @param target the container
  313. * @return the alignment >= 0.0f && <= 1.0f
  314. * @exception AWTError if the target isn't the container specified to the
  315. * BoxLayout constructor
  316. */
  317. public synchronized float getLayoutAlignmentY(Container target) {
  318. checkContainer(target);
  319. checkRequests();
  320. return yTotal.alignment;
  321. }
  322. /**
  323. * Called by the AWT <!-- XXX CHECK! --> when the specified container
  324. * needs to be laid out.
  325. *
  326. * @param target the container to lay out
  327. *
  328. * @exception AWTError if the target isn't the container specified to the
  329. * BoxLayout constructor
  330. */
  331. public void layoutContainer(Container target) {
  332. checkContainer(target);
  333. int nChildren = target.getComponentCount();
  334. int[] xOffsets = new int[nChildren];
  335. int[] xSpans = new int[nChildren];
  336. int[] yOffsets = new int[nChildren];
  337. int[] ySpans = new int[nChildren];
  338. Dimension alloc = target.getSize();
  339. Insets in = target.getInsets();
  340. alloc.width -= in.left + in.right;
  341. alloc.height -= in.top + in.bottom;
  342. // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
  343. ComponentOrientation o = target.getComponentOrientation();
  344. int absoluteAxis = resolveAxis( axis, o );
  345. boolean ltr = (absoluteAxis != axis) ? o.isLeftToRight() : true;
  346. // determine the child placements
  347. synchronized(this) {
  348. checkRequests();
  349. if (absoluteAxis == X_AXIS) {
  350. SizeRequirements.calculateTiledPositions(alloc.width, xTotal,
  351. xChildren, xOffsets,
  352. xSpans, ltr);
  353. SizeRequirements.calculateAlignedPositions(alloc.height, yTotal,
  354. yChildren, yOffsets,
  355. ySpans);
  356. } else {
  357. SizeRequirements.calculateAlignedPositions(alloc.width, xTotal,
  358. xChildren, xOffsets,
  359. xSpans, ltr);
  360. SizeRequirements.calculateTiledPositions(alloc.height, yTotal,
  361. yChildren, yOffsets,
  362. ySpans);
  363. }
  364. }
  365. // flush changes to the container
  366. for (int i = 0; i < nChildren; i++) {
  367. Component c = target.getComponent(i);
  368. c.setBounds((int) Math.min((long) in.left + (long) xOffsets[i], Integer.MAX_VALUE),
  369. (int) Math.min((long) in.top + (long) yOffsets[i], Integer.MAX_VALUE),
  370. xSpans[i], ySpans[i]);
  371. }
  372. if (dbg != null) {
  373. for (int i = 0; i < nChildren; i++) {
  374. Component c = target.getComponent(i);
  375. dbg.println(c.toString());
  376. dbg.println("X: " + xChildren[i]);
  377. dbg.println("Y: " + yChildren[i]);
  378. }
  379. }
  380. }
  381. void checkContainer(Container target) {
  382. if (this.target != target) {
  383. throw new AWTError("BoxLayout can't be shared");
  384. }
  385. }
  386. void checkRequests() {
  387. if (xChildren == null || yChildren == null) {
  388. // The requests have been invalidated... recalculate
  389. // the request information.
  390. int n = target.getComponentCount();
  391. xChildren = new SizeRequirements[n];
  392. yChildren = new SizeRequirements[n];
  393. for (int i = 0; i < n; i++) {
  394. Component c = target.getComponent(i);
  395. if (!c.isVisible()) {
  396. xChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentX());
  397. yChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentY());
  398. continue;
  399. }
  400. Dimension min = c.getMinimumSize();
  401. Dimension typ = c.getPreferredSize();
  402. Dimension max = c.getMaximumSize();
  403. xChildren[i] = new SizeRequirements(min.width, typ.width,
  404. max.width,
  405. c.getAlignmentX());
  406. yChildren[i] = new SizeRequirements(min.height, typ.height,
  407. max.height,
  408. c.getAlignmentY());
  409. }
  410. // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
  411. int absoluteAxis = resolveAxis(axis,target.getComponentOrientation());
  412. if (absoluteAxis == X_AXIS) {
  413. xTotal = SizeRequirements.getTiledSizeRequirements(xChildren);
  414. yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
  415. } else {
  416. xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
  417. yTotal = SizeRequirements.getTiledSizeRequirements(yChildren);
  418. }
  419. }
  420. }
  421. /**
  422. * Given one of the 4 axis values, resolve it to an absolute axis.
  423. * The relative axis values, PAGE_AXIS and LINE_AXIS are converted
  424. * to their absolute couterpart given the target's ComponentOrientation
  425. * value. The absolute axes, X_AXIS and Y_AXIS are returned unmodified.
  426. *
  427. * @param axis the axis to resolve
  428. * @param o the ComponentOrientation to resolve against
  429. * @return the resolved axis
  430. */
  431. private int resolveAxis( int axis, ComponentOrientation o ) {
  432. int absoluteAxis;
  433. if( axis == LINE_AXIS ) {
  434. absoluteAxis = o.isHorizontal() ? X_AXIS : Y_AXIS;
  435. } else if( axis == PAGE_AXIS ) {
  436. absoluteAxis = o.isHorizontal() ? Y_AXIS : X_AXIS;
  437. } else {
  438. absoluteAxis = axis;
  439. }
  440. return absoluteAxis;
  441. }
  442. private int axis;
  443. private Container target;
  444. private transient SizeRequirements[] xChildren;
  445. private transient SizeRequirements[] yChildren;
  446. private transient SizeRequirements xTotal;
  447. private transient SizeRequirements yTotal;
  448. private transient PrintStream dbg;
  449. }