1. /*
  2. * @(#)BoxLayout.java 1.32 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.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. * <strong>Warning:</strong>
  95. * Serialized objects of this class will not be compatible with
  96. * future Swing releases. The current serialization support is
  97. * appropriate for short term storage or RMI between applications running
  98. * the same version of Swing. As of 1.4, support for long term storage
  99. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  100. * has been added to the <code>java.beans</code> package.
  101. * Please see {@link java.beans.XMLEncoder}.
  102. *
  103. * @see Box
  104. * @see java.awt.ComponentOrientation
  105. * @see JComponent#getAlignmentX
  106. * @see JComponent#getAlignmentY
  107. *
  108. * @author Timothy Prinzing
  109. * @version 1.32 01/23/03
  110. */
  111. public class BoxLayout implements LayoutManager2, Serializable {
  112. /**
  113. * Specifies that components should be laid out left to right.
  114. */
  115. public static final int X_AXIS = 0;
  116. /**
  117. * Specifies that components should be laid out top to bottom.
  118. */
  119. public static final int Y_AXIS = 1;
  120. /**
  121. * Specifies that components should be laid out in the direction of
  122. * a line of text as determined by the target container's
  123. * <code>ComponentOrientation</code> property.
  124. */
  125. public static final int LINE_AXIS = 2;
  126. /**
  127. * Specifies that components should be laid out in the direction that
  128. * lines flow across a page as determined by the target container's
  129. * <code>ComponentOrientation</code> property.
  130. */
  131. public static final int PAGE_AXIS = 3;
  132. /**
  133. * Creates a layout manager that will lay out components along the
  134. * given axis.
  135. *
  136. * @param target the container that needs to be laid out
  137. * @param axis the axis to lay out components along. Can be one of:
  138. * <code>BoxLayout.X_AXIS</code>,
  139. * <code>BoxLayout.Y_AXIS</code>,
  140. * <code>BoxLayout.LINE_AXIS</code> or
  141. * <code>BoxLayout.PAGE_AXIS</code>
  142. *
  143. * @exception AWTError if the value of <code>axis</code> is invalid
  144. */
  145. public BoxLayout(Container target, int axis) {
  146. if (axis != X_AXIS && axis != Y_AXIS &&
  147. axis != LINE_AXIS && axis != PAGE_AXIS) {
  148. throw new AWTError("Invalid axis");
  149. }
  150. this.axis = axis;
  151. this.target = target;
  152. }
  153. /**
  154. * Constructs a BoxLayout that
  155. * produces debugging messages.
  156. *
  157. * @param target the container that needs to be laid out
  158. * @param axis the axis to lay out components along. Can be one of:
  159. * <code>BoxLayout.X_AXIS</code>,
  160. * <code>BoxLayout.Y_AXIS</code>,
  161. * <code>BoxLayout.LINE_AXIS</code> or
  162. * <code>BoxLayout.PAGE_AXIS</code>
  163. *
  164. * @param dbg the stream to which debugging messages should be sent,
  165. * null if none
  166. */
  167. BoxLayout(Container target, int axis, PrintStream dbg) {
  168. this(target, axis);
  169. this.dbg = dbg;
  170. }
  171. /**
  172. * Indicates that a child has changed its layout related information,
  173. * and thus any cached calculations should be flushed.
  174. * <p>
  175. * This method is called by AWT when the invalidate method is called
  176. * on the Container. Since the invalidate method may be called
  177. * asynchronously to the event thread, this method may be called
  178. * asynchronously.
  179. *
  180. * @param target the affected container
  181. *
  182. * @exception AWTError if the target isn't the container specified to the
  183. * BoxLayout constructor
  184. */
  185. public synchronized void invalidateLayout(Container target) {
  186. checkContainer(target);
  187. xChildren = null;
  188. yChildren = null;
  189. xTotal = null;
  190. yTotal = null;
  191. }
  192. /**
  193. * Not used by this class.
  194. *
  195. * @param name the name of the component
  196. * @param comp the component
  197. */
  198. public void addLayoutComponent(String name, Component comp) {
  199. }
  200. /**
  201. * Not used by this class.
  202. *
  203. * @param comp the component
  204. */
  205. public void removeLayoutComponent(Component comp) {
  206. }
  207. /**
  208. * Not used by this class.
  209. *
  210. * @param comp the component
  211. * @param constraints constraints
  212. */
  213. public void addLayoutComponent(Component comp, Object constraints) {
  214. }
  215. /**
  216. * Returns the preferred dimensions for this layout, given the components
  217. * in the specified target container.
  218. *
  219. * @param target the container that needs to be laid out
  220. * @return the dimensions >= 0 && <= Integer.MAX_VALUE
  221. * @exception AWTError if the target isn't the container specified to the
  222. * BoxLayout constructor
  223. * @see Container
  224. * @see #minimumLayoutSize
  225. * @see #maximumLayoutSize
  226. */
  227. public Dimension preferredLayoutSize(Container target) {
  228. Dimension size;
  229. synchronized(this) {
  230. checkContainer(target);
  231. checkRequests();
  232. size = new Dimension(xTotal.preferred, yTotal.preferred);
  233. }
  234. Insets insets = target.getInsets();
  235. size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
  236. size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
  237. return size;
  238. }
  239. /**
  240. * Returns the minimum dimensions needed to lay out the components
  241. * contained in the specified target container.
  242. *
  243. * @param target the container that needs to be laid out
  244. * @return the dimensions >= 0 && <= Integer.MAX_VALUE
  245. * @exception AWTError if the target isn't the container specified to the
  246. * BoxLayout constructor
  247. * @see #preferredLayoutSize
  248. * @see #maximumLayoutSize
  249. */
  250. public Dimension minimumLayoutSize(Container target) {
  251. Dimension size;
  252. synchronized(this) {
  253. checkContainer(target);
  254. checkRequests();
  255. size = new Dimension(xTotal.minimum, yTotal.minimum);
  256. }
  257. Insets insets = target.getInsets();
  258. size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
  259. size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
  260. return size;
  261. }
  262. /**
  263. * Returns the maximum dimensions the target container can use
  264. * to lay out the components it contains.
  265. *
  266. * @param target the container that needs to be laid out
  267. * @return the dimenions >= 0 && <= Integer.MAX_VALUE
  268. * @exception AWTError if the target isn't the container specified to the
  269. * BoxLayout constructor
  270. * @see #preferredLayoutSize
  271. * @see #minimumLayoutSize
  272. */
  273. public Dimension maximumLayoutSize(Container target) {
  274. Dimension size;
  275. synchronized(this) {
  276. checkContainer(target);
  277. checkRequests();
  278. size = new Dimension(xTotal.maximum, yTotal.maximum);
  279. }
  280. Insets insets = target.getInsets();
  281. size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
  282. size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
  283. return size;
  284. }
  285. /**
  286. * Returns the alignment along the X axis for the container.
  287. * If the box is horizontal, the default
  288. * alignment will be returned. Otherwise, the alignment needed
  289. * to place the children along the X axis will be returned.
  290. *
  291. * @param target the container
  292. * @return the alignment >= 0.0f && <= 1.0f
  293. * @exception AWTError if the target isn't the container specified to the
  294. * BoxLayout constructor
  295. */
  296. public synchronized float getLayoutAlignmentX(Container target) {
  297. checkContainer(target);
  298. checkRequests();
  299. return xTotal.alignment;
  300. }
  301. /**
  302. * Returns the alignment along the Y axis for the container.
  303. * If the box is vertical, the default
  304. * alignment will be returned. Otherwise, the alignment needed
  305. * to place the children along the Y axis will be returned.
  306. *
  307. * @param target the container
  308. * @return the alignment >= 0.0f && <= 1.0f
  309. * @exception AWTError if the target isn't the container specified to the
  310. * BoxLayout constructor
  311. */
  312. public synchronized float getLayoutAlignmentY(Container target) {
  313. checkContainer(target);
  314. checkRequests();
  315. return yTotal.alignment;
  316. }
  317. /**
  318. * Called by the AWT <!-- XXX CHECK! --> when the specified container
  319. * needs to be laid out.
  320. *
  321. * @param target the container to lay out
  322. *
  323. * @exception AWTError if the target isn't the container specified to the
  324. * BoxLayout constructor
  325. */
  326. public void layoutContainer(Container target) {
  327. checkContainer(target);
  328. int nChildren = target.getComponentCount();
  329. int[] xOffsets = new int[nChildren];
  330. int[] xSpans = new int[nChildren];
  331. int[] yOffsets = new int[nChildren];
  332. int[] ySpans = new int[nChildren];
  333. Dimension alloc = target.getSize();
  334. Insets in = target.getInsets();
  335. alloc.width -= in.left + in.right;
  336. alloc.height -= in.top + in.bottom;
  337. // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
  338. ComponentOrientation o = target.getComponentOrientation();
  339. int absoluteAxis = resolveAxis( axis, o );
  340. boolean ltr = (absoluteAxis != axis) ? o.isLeftToRight() : true;
  341. // determine the child placements
  342. synchronized(this) {
  343. checkRequests();
  344. if (absoluteAxis == X_AXIS) {
  345. SizeRequirements.calculateTiledPositions(alloc.width, xTotal,
  346. xChildren, xOffsets,
  347. xSpans, ltr);
  348. SizeRequirements.calculateAlignedPositions(alloc.height, yTotal,
  349. yChildren, yOffsets,
  350. ySpans);
  351. } else {
  352. SizeRequirements.calculateAlignedPositions(alloc.width, xTotal,
  353. xChildren, xOffsets,
  354. xSpans, ltr);
  355. SizeRequirements.calculateTiledPositions(alloc.height, yTotal,
  356. yChildren, yOffsets,
  357. ySpans);
  358. }
  359. }
  360. // flush changes to the container
  361. for (int i = 0; i < nChildren; i++) {
  362. Component c = target.getComponent(i);
  363. c.setBounds((int) Math.min((long) in.left + (long) xOffsets[i], Integer.MAX_VALUE),
  364. (int) Math.min((long) in.top + (long) yOffsets[i], Integer.MAX_VALUE),
  365. xSpans[i], ySpans[i]);
  366. }
  367. if (dbg != null) {
  368. for (int i = 0; i < nChildren; i++) {
  369. Component c = target.getComponent(i);
  370. dbg.println(c.toString());
  371. dbg.println("X: " + xChildren[i]);
  372. dbg.println("Y: " + yChildren[i]);
  373. }
  374. }
  375. }
  376. void checkContainer(Container target) {
  377. if (this.target != target) {
  378. throw new AWTError("BoxLayout can't be shared");
  379. }
  380. }
  381. void checkRequests() {
  382. if (xChildren == null || yChildren == null) {
  383. // The requests have been invalidated... recalculate
  384. // the request information.
  385. int n = target.getComponentCount();
  386. xChildren = new SizeRequirements[n];
  387. yChildren = new SizeRequirements[n];
  388. for (int i = 0; i < n; i++) {
  389. Component c = target.getComponent(i);
  390. if (!c.isVisible()) {
  391. xChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentX());
  392. yChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentY());
  393. continue;
  394. }
  395. Dimension min = c.getMinimumSize();
  396. Dimension typ = c.getPreferredSize();
  397. Dimension max = c.getMaximumSize();
  398. xChildren[i] = new SizeRequirements(min.width, typ.width,
  399. max.width,
  400. c.getAlignmentX());
  401. yChildren[i] = new SizeRequirements(min.height, typ.height,
  402. max.height,
  403. c.getAlignmentY());
  404. }
  405. // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
  406. int absoluteAxis = resolveAxis(axis,target.getComponentOrientation());
  407. if (absoluteAxis == X_AXIS) {
  408. xTotal = SizeRequirements.getTiledSizeRequirements(xChildren);
  409. yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
  410. } else {
  411. xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
  412. yTotal = SizeRequirements.getTiledSizeRequirements(yChildren);
  413. }
  414. }
  415. }
  416. /**
  417. * Given one of the 4 axis values, resolve it to an absolute axis.
  418. * The relative axis values, PAGE_AXIS and LINE_AXIS are converted
  419. * to their absolute couterpart given the target's ComponentOrientation
  420. * value. The absolute axes, X_AXIS and Y_AXIS are returned unmodified.
  421. *
  422. * @param axis the axis to resolve
  423. * @param o the ComponentOrientation to resolve against
  424. * @return the resolved axis
  425. */
  426. private int resolveAxis( int axis, ComponentOrientation o ) {
  427. int absoluteAxis;
  428. if( axis == LINE_AXIS ) {
  429. absoluteAxis = o.isHorizontal() ? X_AXIS : Y_AXIS;
  430. } else if( axis == PAGE_AXIS ) {
  431. absoluteAxis = o.isHorizontal() ? Y_AXIS : X_AXIS;
  432. } else {
  433. absoluteAxis = axis;
  434. }
  435. return absoluteAxis;
  436. }
  437. private int axis;
  438. private Container target;
  439. private transient SizeRequirements[] xChildren;
  440. private transient SizeRequirements[] yChildren;
  441. private transient SizeRequirements xTotal;
  442. private transient SizeRequirements yTotal;
  443. private transient PrintStream dbg;
  444. }