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