001 /*
002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing;
027
028 import java.awt.*;
029 import java.beans.ConstructorProperties;
030 import java.io.Serializable;
031 import java.io.PrintStream;
032
033 /**
034 * A layout manager that allows multiple components to be laid out either
035 * vertically or horizontally. The components will not wrap so, for
036 * example, a vertical arrangement of components will stay vertically
037 * arranged when the frame is resized.
038 * <TABLE ALIGN="RIGHT" BORDER="0" SUMMARY="layout">
039 * <TR>
040 * <TD ALIGN="CENTER">
041 * <P ALIGN="CENTER"><IMG SRC="doc-files/BoxLayout-1.gif"
042 * alt="The following text describes this graphic."
043 * WIDTH="191" HEIGHT="201" ALIGN="BOTTOM" BORDER="0">
044 * </TD>
045 * </TR>
046 * </TABLE>
047 * <p>
048 * Nesting multiple panels with different combinations of horizontal and
049 * vertical gives an effect similar to GridBagLayout, without the
050 * complexity. The diagram shows two panels arranged horizontally, each
051 * of which contains 3 components arranged vertically.
052 *
053 * <p> The BoxLayout manager is constructed with an axis parameter that
054 * specifies the type of layout that will be done. There are four choices:
055 *
056 * <blockquote><b><tt>X_AXIS</tt></b> - Components are laid out horizontally
057 * from left to right.</blockquote>
058 *
059 * <blockquote><b><tt>Y_AXIS</tt></b> - Components are laid out vertically
060 * from top to bottom.</blockquote>
061 *
062 * <blockquote><b><tt>LINE_AXIS</tt></b> - Components are laid out the way
063 * words are laid out in a line, based on the container's
064 * <tt>ComponentOrientation</tt> property. If the container's
065 * <tt>ComponentOrientation</tt> is horizontal then components are laid out
066 * horizontally, otherwise they are laid out vertically. For horizontal
067 * orientations, if the container's <tt>ComponentOrientation</tt> is left to
068 * right then components are laid out left to right, otherwise they are laid
069 * out right to left. For vertical orientations components are always laid out
070 * from top to bottom.</blockquote>
071 *
072 * <blockquote><b><tt>PAGE_AXIS</tt></b> - Components are laid out the way
073 * text lines are laid out on a page, based on the container's
074 * <tt>ComponentOrientation</tt> property. If the container's
075 * <tt>ComponentOrientation</tt> is horizontal then components are laid out
076 * vertically, otherwise they are laid out horizontally. For horizontal
077 * orientations, if the container's <tt>ComponentOrientation</tt> is left to
078 * right then components are laid out left to right, otherwise they are laid
079 * out right to left. For vertical orientations components are always
080 * laid out from top to bottom.</blockquote>
081 * <p>
082 * For all directions, components are arranged in the same order as they were
083 * added to the container.
084 * <p>
085 * BoxLayout attempts to arrange components
086 * at their preferred widths (for horizontal layout)
087 * or heights (for vertical layout).
088 * For a horizontal layout,
089 * if not all the components are the same height,
090 * BoxLayout attempts to make all the components
091 * as high as the highest component.
092 * If that's not possible for a particular component,
093 * then BoxLayout aligns that component vertically,
094 * according to the component's Y alignment.
095 * By default, a component has a Y alignment of 0.5,
096 * which means that the vertical center of the component
097 * should have the same Y coordinate as
098 * the vertical centers of other components with 0.5 Y alignment.
099 * <p>
100 * Similarly, for a vertical layout,
101 * BoxLayout attempts to make all components in the column
102 * as wide as the widest component.
103 * If that fails, it aligns them horizontally
104 * according to their X alignments. For <code>PAGE_AXIS</code> layout,
105 * horizontal alignment is done based on the leading edge of the component.
106 * In other words, an X alignment value of 0.0 means the left edge of a
107 * component if the container's <code>ComponentOrientation</code> is left to
108 * right and it means the right edge of the component otherwise.
109 * <p>
110 * Instead of using BoxLayout directly, many programs use the Box class.
111 * The Box class is a lightweight container that uses a BoxLayout.
112 * It also provides handy methods to help you use BoxLayout well.
113 * Adding components to multiple nested boxes is a powerful way to get
114 * the arrangement you want.
115 * <p>
116 * For further information and examples see
117 * <a
118 href="http://java.sun.com/docs/books/tutorial/uiswing/layout/box.html">How to Use BoxLayout</a>,
119 * a section in <em>The Java Tutorial.</em>
120 * <p>
121 * <strong>Warning:</strong>
122 * Serialized objects of this class will not be compatible with
123 * future Swing releases. The current serialization support is
124 * appropriate for short term storage or RMI between applications running
125 * the same version of Swing. As of 1.4, support for long term storage
126 * of all JavaBeans<sup><font size="-2">TM</font></sup>
127 * has been added to the <code>java.beans</code> package.
128 * Please see {@link java.beans.XMLEncoder}.
129 *
130 * @see Box
131 * @see java.awt.ComponentOrientation
132 * @see JComponent#getAlignmentX
133 * @see JComponent#getAlignmentY
134 *
135 * @author Timothy Prinzing
136 * @version 1.42 05/05/07
137 */
138 public 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 && axis != LINE_AXIS
180 && 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
306 + (long) insets.left + (long) insets.right,
307 Integer.MAX_VALUE);
308 size.height = (int) Math.min((long) size.height
309 + (long) insets.top + (long) insets.bottom,
310 Integer.MAX_VALUE);
311 return size;
312 }
313
314 /**
315 * Returns the minimum dimensions needed to lay out the components
316 * contained in the specified target container.
317 *
318 * @param target the container that needs to be laid out
319 * @return the dimensions >= 0 && <= Integer.MAX_VALUE
320 * @exception AWTError if the target isn't the container specified to the
321 * BoxLayout constructor
322 * @see #preferredLayoutSize
323 * @see #maximumLayoutSize
324 */
325 public Dimension minimumLayoutSize(Container target) {
326 Dimension size;
327 synchronized (this ) {
328 checkContainer(target);
329 checkRequests();
330 size = new Dimension(xTotal.minimum, yTotal.minimum);
331 }
332
333 Insets insets = target.getInsets();
334 size.width = (int) Math.min((long) size.width
335 + (long) insets.left + (long) insets.right,
336 Integer.MAX_VALUE);
337 size.height = (int) Math.min((long) size.height
338 + (long) insets.top + (long) insets.bottom,
339 Integer.MAX_VALUE);
340 return size;
341 }
342
343 /**
344 * Returns the maximum dimensions the target container can use
345 * to lay out the components it contains.
346 *
347 * @param target the container that needs to be laid out
348 * @return the dimenions >= 0 && <= Integer.MAX_VALUE
349 * @exception AWTError if the target isn't the container specified to the
350 * BoxLayout constructor
351 * @see #preferredLayoutSize
352 * @see #minimumLayoutSize
353 */
354 public Dimension maximumLayoutSize(Container target) {
355 Dimension size;
356 synchronized (this ) {
357 checkContainer(target);
358 checkRequests();
359 size = new Dimension(xTotal.maximum, yTotal.maximum);
360 }
361
362 Insets insets = target.getInsets();
363 size.width = (int) Math.min((long) size.width
364 + (long) insets.left + (long) insets.right,
365 Integer.MAX_VALUE);
366 size.height = (int) Math.min((long) size.height
367 + (long) insets.top + (long) insets.bottom,
368 Integer.MAX_VALUE);
369 return size;
370 }
371
372 /**
373 * Returns the alignment along the X axis for the container.
374 * If the box is horizontal, the default
375 * alignment will be returned. Otherwise, the alignment needed
376 * to place the children along the X axis will be returned.
377 *
378 * @param target the container
379 * @return the alignment >= 0.0f && <= 1.0f
380 * @exception AWTError if the target isn't the container specified to the
381 * BoxLayout constructor
382 */
383 public synchronized float getLayoutAlignmentX(Container target) {
384 checkContainer(target);
385 checkRequests();
386 return xTotal.alignment;
387 }
388
389 /**
390 * Returns the alignment along the Y axis for the container.
391 * If the box is vertical, the default
392 * alignment will be returned. Otherwise, the alignment needed
393 * to place the children along the Y axis will be returned.
394 *
395 * @param target the container
396 * @return the alignment >= 0.0f && <= 1.0f
397 * @exception AWTError if the target isn't the container specified to the
398 * BoxLayout constructor
399 */
400 public synchronized float getLayoutAlignmentY(Container target) {
401 checkContainer(target);
402 checkRequests();
403 return yTotal.alignment;
404 }
405
406 /**
407 * Called by the AWT <!-- XXX CHECK! --> when the specified container
408 * needs to be laid out.
409 *
410 * @param target the container to lay out
411 *
412 * @exception AWTError if the target isn't the container specified to the
413 * BoxLayout constructor
414 */
415 public void layoutContainer(Container target) {
416 checkContainer(target);
417 int nChildren = target.getComponentCount();
418 int[] xOffsets = new int[nChildren];
419 int[] xSpans = new int[nChildren];
420 int[] yOffsets = new int[nChildren];
421 int[] ySpans = new int[nChildren];
422
423 Dimension alloc = target.getSize();
424 Insets in = target.getInsets();
425 alloc.width -= in.left + in.right;
426 alloc.height -= in.top + in.bottom;
427
428 // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
429 ComponentOrientation o = target.getComponentOrientation();
430 int absoluteAxis = resolveAxis(axis, o);
431 boolean ltr = (absoluteAxis != axis) ? o.isLeftToRight() : true;
432
433 // determine the child placements
434 synchronized (this ) {
435 checkRequests();
436
437 if (absoluteAxis == X_AXIS) {
438 SizeRequirements.calculateTiledPositions(alloc.width,
439 xTotal, xChildren, xOffsets, xSpans, ltr);
440 SizeRequirements.calculateAlignedPositions(
441 alloc.height, yTotal, yChildren, yOffsets,
442 ySpans);
443 } else {
444 SizeRequirements.calculateAlignedPositions(alloc.width,
445 xTotal, xChildren, xOffsets, xSpans, ltr);
446 SizeRequirements.calculateTiledPositions(alloc.height,
447 yTotal, yChildren, yOffsets, ySpans);
448 }
449 }
450
451 // flush changes to the container
452 for (int i = 0; i < nChildren; i++) {
453 Component c = target.getComponent(i);
454 c.setBounds((int) Math.min((long) in.left
455 + (long) xOffsets[i], Integer.MAX_VALUE),
456 (int) Math.min((long) in.top + (long) yOffsets[i],
457 Integer.MAX_VALUE), xSpans[i], ySpans[i]);
458
459 }
460 if (dbg != null) {
461 for (int i = 0; i < nChildren; i++) {
462 Component c = target.getComponent(i);
463 dbg.println(c.toString());
464 dbg.println("X: " + xChildren[i]);
465 dbg.println("Y: " + yChildren[i]);
466 }
467 }
468
469 }
470
471 void checkContainer(Container target) {
472 if (this .target != target) {
473 throw new AWTError("BoxLayout can't be shared");
474 }
475 }
476
477 void checkRequests() {
478 if (xChildren == null || yChildren == null) {
479 // The requests have been invalidated... recalculate
480 // the request information.
481 int n = target.getComponentCount();
482 xChildren = new SizeRequirements[n];
483 yChildren = new SizeRequirements[n];
484 for (int i = 0; i < n; i++) {
485 Component c = target.getComponent(i);
486 if (!c.isVisible()) {
487 xChildren[i] = new SizeRequirements(0, 0, 0, c
488 .getAlignmentX());
489 yChildren[i] = new SizeRequirements(0, 0, 0, c
490 .getAlignmentY());
491 continue;
492 }
493 Dimension min = c.getMinimumSize();
494 Dimension typ = c.getPreferredSize();
495 Dimension max = c.getMaximumSize();
496 xChildren[i] = new SizeRequirements(min.width,
497 typ.width, max.width, c.getAlignmentX());
498 yChildren[i] = new SizeRequirements(min.height,
499 typ.height, max.height, c.getAlignmentY());
500 }
501
502 // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
503 int absoluteAxis = resolveAxis(axis, target
504 .getComponentOrientation());
505
506 if (absoluteAxis == X_AXIS) {
507 xTotal = SizeRequirements
508 .getTiledSizeRequirements(xChildren);
509 yTotal = SizeRequirements
510 .getAlignedSizeRequirements(yChildren);
511 } else {
512 xTotal = SizeRequirements
513 .getAlignedSizeRequirements(xChildren);
514 yTotal = SizeRequirements
515 .getTiledSizeRequirements(yChildren);
516 }
517 }
518 }
519
520 /**
521 * Given one of the 4 axis values, resolve it to an absolute axis.
522 * The relative axis values, PAGE_AXIS and LINE_AXIS are converted
523 * to their absolute couterpart given the target's ComponentOrientation
524 * value. The absolute axes, X_AXIS and Y_AXIS are returned unmodified.
525 *
526 * @param axis the axis to resolve
527 * @param o the ComponentOrientation to resolve against
528 * @return the resolved axis
529 */
530 private int resolveAxis(int axis, ComponentOrientation o) {
531 int absoluteAxis;
532 if (axis == LINE_AXIS) {
533 absoluteAxis = o.isHorizontal() ? X_AXIS : Y_AXIS;
534 } else if (axis == PAGE_AXIS) {
535 absoluteAxis = o.isHorizontal() ? Y_AXIS : X_AXIS;
536 } else {
537 absoluteAxis = axis;
538 }
539 return absoluteAxis;
540 }
541
542 private int axis;
543 private Container target;
544
545 private transient SizeRequirements[] xChildren;
546 private transient SizeRequirements[] yChildren;
547 private transient SizeRequirements xTotal;
548 private transient SizeRequirements yTotal;
549
550 private transient PrintStream dbg;
551 }
|