001 /*
002 * Copyright 1995-2006 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 package java.awt;
026
027 import java.io.ObjectInputStream;
028 import java.io.IOException;
029
030 /**
031 * A flow layout arranges components in a directional flow, much
032 * like lines of text in a paragraph. The flow direction is
033 * determined by the container's <code>componentOrientation</code>
034 * property and may be one of two values:
035 * <ul>
036 * <li><code>ComponentOrientation.LEFT_TO_RIGHT</code>
037 * <li><code>ComponentOrientation.RIGHT_TO_LEFT</code>
038 * </ul>
039 * Flow layouts are typically used
040 * to arrange buttons in a panel. It arranges buttons
041 * horizontally until no more buttons fit on the same line.
042 * The line alignment is determined by the <code>align</code>
043 * property. The possible values are:
044 * <ul>
045 * <li>{@link #LEFT LEFT}
046 * <li>{@link #RIGHT RIGHT}
047 * <li>{@link #CENTER CENTER}
048 * <li>{@link #LEADING LEADING}
049 * <li>{@link #TRAILING TRAILING}
050 * </ul>
051 * <p>
052 * For example, the following picture shows an applet using the flow
053 * layout manager (its default layout manager) to position three buttons:
054 * <p>
055 * <img src="doc-files/FlowLayout-1.gif"
056 * ALT="Graphic of Layout for Three Buttons"
057 * ALIGN=center HSPACE=10 VSPACE=7>
058 * <p>
059 * Here is the code for this applet:
060 * <p>
061 * <hr><blockquote><pre>
062 * import java.awt.*;
063 * import java.applet.Applet;
064 *
065 * public class myButtons extends Applet {
066 * Button button1, button2, button3;
067 * public void init() {
068 * button1 = new Button("Ok");
069 * button2 = new Button("Open");
070 * button3 = new Button("Close");
071 * add(button1);
072 * add(button2);
073 * add(button3);
074 * }
075 * }
076 * </pre></blockquote><hr>
077 * <p>
078 * A flow layout lets each component assume its natural (preferred) size.
079 *
080 * @version 1.65, 05/05/07
081 * @author Arthur van Hoff
082 * @author Sami Shaio
083 * @since JDK1.0
084 * @see ComponentOrientation
085 */
086 public class FlowLayout implements LayoutManager, java.io.Serializable {
087
088 /**
089 * This value indicates that each row of components
090 * should be left-justified.
091 */
092 public static final int LEFT = 0;
093
094 /**
095 * This value indicates that each row of components
096 * should be centered.
097 */
098 public static final int CENTER = 1;
099
100 /**
101 * This value indicates that each row of components
102 * should be right-justified.
103 */
104 public static final int RIGHT = 2;
105
106 /**
107 * This value indicates that each row of components
108 * should be justified to the leading edge of the container's
109 * orientation, for example, to the left in left-to-right orientations.
110 *
111 * @see java.awt.Component#getComponentOrientation
112 * @see java.awt.ComponentOrientation
113 * @since 1.2
114 */
115 public static final int LEADING = 3;
116
117 /**
118 * This value indicates that each row of components
119 * should be justified to the trailing edge of the container's
120 * orientation, for example, to the right in left-to-right orientations.
121 *
122 * @see java.awt.Component#getComponentOrientation
123 * @see java.awt.ComponentOrientation
124 * @since 1.2
125 */
126 public static final int TRAILING = 4;
127
128 /**
129 * <code>align</code> is the property that determines
130 * how each row distributes empty space.
131 * It can be one of the following values:
132 * <ul>
133 * <code>LEFT</code>
134 * <code>RIGHT</code>
135 * <code>CENTER</code>
136 * </ul>
137 *
138 * @serial
139 * @see #getAlignment
140 * @see #setAlignment
141 */
142 int align; // This is for 1.1 serialization compatibility
143
144 /**
145 * <code>newAlign</code> is the property that determines
146 * how each row distributes empty space for the Java 2 platform,
147 * v1.2 and greater.
148 * It can be one of the following three values:
149 * <ul>
150 * <code>LEFT</code>
151 * <code>RIGHT</code>
152 * <code>CENTER</code>
153 * <code>LEADING</code>
154 * <code>TRAILING</code>
155 * </ul>
156 *
157 * @serial
158 * @since 1.2
159 * @see #getAlignment
160 * @see #setAlignment
161 */
162 int newAlign; // This is the one we actually use
163
164 /**
165 * The flow layout manager allows a seperation of
166 * components with gaps. The horizontal gap will
167 * specify the space between components and between
168 * the components and the borders of the
169 * <code>Container</code>.
170 *
171 * @serial
172 * @see #getHgap()
173 * @see #setHgap(int)
174 */
175 int hgap;
176
177 /**
178 * The flow layout manager allows a seperation of
179 * components with gaps. The vertical gap will
180 * specify the space between rows and between the
181 * the rows and the borders of the <code>Container</code>.
182 *
183 * @serial
184 * @see #getHgap()
185 * @see #setHgap(int)
186 */
187 int vgap;
188
189 /**
190 * If true, components will be aligned on their baseline.
191 */
192 private boolean alignOnBaseline;
193
194 /*
195 * JDK 1.1 serialVersionUID
196 */
197 private static final long serialVersionUID = -7262534875583282631L;
198
199 /**
200 * Constructs a new <code>FlowLayout</code> with a centered alignment and a
201 * default 5-unit horizontal and vertical gap.
202 */
203 public FlowLayout() {
204 this (CENTER, 5, 5);
205 }
206
207 /**
208 * Constructs a new <code>FlowLayout</code> with the specified
209 * alignment and a default 5-unit horizontal and vertical gap.
210 * The value of the alignment argument must be one of
211 * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
212 * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>,
213 * or <code>FlowLayout.TRAILING</code>.
214 * @param align the alignment value
215 */
216 public FlowLayout(int align) {
217 this (align, 5, 5);
218 }
219
220 /**
221 * Creates a new flow layout manager with the indicated alignment
222 * and the indicated horizontal and vertical gaps.
223 * <p>
224 * The value of the alignment argument must be one of
225 * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
226 * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>,
227 * or <code>FlowLayout.TRAILING</code>.
228 * @param align the alignment value
229 * @param hgap the horizontal gap between components
230 * and between the components and the
231 * borders of the <code>Container</code>
232 * @param vgap the vertical gap between components
233 * and between the components and the
234 * borders of the <code>Container</code>
235 */
236 public FlowLayout(int align, int hgap, int vgap) {
237 this .hgap = hgap;
238 this .vgap = vgap;
239 setAlignment(align);
240 }
241
242 /**
243 * Gets the alignment for this layout.
244 * Possible values are <code>FlowLayout.LEFT</code>,
245 * <code>FlowLayout.RIGHT</code>, <code>FlowLayout.CENTER</code>,
246 * <code>FlowLayout.LEADING</code>,
247 * or <code>FlowLayout.TRAILING</code>.
248 * @return the alignment value for this layout
249 * @see java.awt.FlowLayout#setAlignment
250 * @since JDK1.1
251 */
252 public int getAlignment() {
253 return newAlign;
254 }
255
256 /**
257 * Sets the alignment for this layout.
258 * Possible values are
259 * <ul>
260 * <li><code>FlowLayout.LEFT</code>
261 * <li><code>FlowLayout.RIGHT</code>
262 * <li><code>FlowLayout.CENTER</code>
263 * <li><code>FlowLayout.LEADING</code>
264 * <li><code>FlowLayout.TRAILING</code>
265 * </ul>
266 * @param align one of the alignment values shown above
267 * @see #getAlignment()
268 * @since JDK1.1
269 */
270 public void setAlignment(int align) {
271 this .newAlign = align;
272
273 // this.align is used only for serialization compatibility,
274 // so set it to a value compatible with the 1.1 version
275 // of the class
276
277 switch (align) {
278 case LEADING:
279 this .align = LEFT;
280 break;
281 case TRAILING:
282 this .align = RIGHT;
283 break;
284 default:
285 this .align = align;
286 break;
287 }
288 }
289
290 /**
291 * Gets the horizontal gap between components
292 * and between the components and the borders
293 * of the <code>Container</code>
294 *
295 * @return the horizontal gap between components
296 * and between the components and the borders
297 * of the <code>Container</code>
298 * @see java.awt.FlowLayout#setHgap
299 * @since JDK1.1
300 */
301 public int getHgap() {
302 return hgap;
303 }
304
305 /**
306 * Sets the horizontal gap between components and
307 * between the components and the borders of the
308 * <code>Container</code>.
309 *
310 * @param hgap the horizontal gap between components
311 * and between the components and the borders
312 * of the <code>Container</code>
313 * @see java.awt.FlowLayout#getHgap
314 * @since JDK1.1
315 */
316 public void setHgap(int hgap) {
317 this .hgap = hgap;
318 }
319
320 /**
321 * Gets the vertical gap between components and
322 * between the components and the borders of the
323 * <code>Container</code>.
324 *
325 * @return the vertical gap between components
326 * and between the components and the borders
327 * of the <code>Container</code>
328 * @see java.awt.FlowLayout#setVgap
329 * @since JDK1.1
330 */
331 public int getVgap() {
332 return vgap;
333 }
334
335 /**
336 * Sets the vertical gap between components and between
337 * the components and the borders of the <code>Container</code>.
338 *
339 * @param vgap the vertical gap between components
340 * and between the components and the borders
341 * of the <code>Container</code>
342 * @see java.awt.FlowLayout#getVgap
343 * @since JDK1.1
344 */
345 public void setVgap(int vgap) {
346 this .vgap = vgap;
347 }
348
349 /**
350 * Sets whether or not components should be vertically aligned along their
351 * baseline. Components that do not have a baseline will be centered.
352 * The default is false.
353 *
354 * @param alignOnBaseline whether or not components should be
355 * vertically aligned on their baseline
356 * @since 1.6
357 */
358 public void setAlignOnBaseline(boolean alignOnBaseline) {
359 this .alignOnBaseline = alignOnBaseline;
360 }
361
362 /**
363 * Returns true if components are to be vertically aligned along
364 * their baseline. The default is false.
365 *
366 * @return true if components are to be vertically aligned along
367 * their baseline
368 * @since 1.6
369 */
370 public boolean getAlignOnBaseline() {
371 return alignOnBaseline;
372 }
373
374 /**
375 * Adds the specified component to the layout.
376 * Not used by this class.
377 * @param name the name of the component
378 * @param comp the component to be added
379 */
380 public void addLayoutComponent(String name, Component comp) {
381 }
382
383 /**
384 * Removes the specified component from the layout.
385 * Not used by this class.
386 * @param comp the component to remove
387 * @see java.awt.Container#removeAll
388 */
389 public void removeLayoutComponent(Component comp) {
390 }
391
392 /**
393 * Returns the preferred dimensions for this layout given the
394 * <i>visible</i> components in the specified target container.
395 *
396 * @param target the container that needs to be laid out
397 * @return the preferred dimensions to lay out the
398 * subcomponents of the specified container
399 * @see Container
400 * @see #minimumLayoutSize
401 * @see java.awt.Container#getPreferredSize
402 */
403 public Dimension preferredLayoutSize(Container target) {
404 synchronized (target.getTreeLock()) {
405 Dimension dim = new Dimension(0, 0);
406 int nmembers = target.getComponentCount();
407 boolean firstVisibleComponent = true;
408 boolean useBaseline = getAlignOnBaseline();
409 int maxAscent = 0;
410 int maxDescent = 0;
411
412 for (int i = 0; i < nmembers; i++) {
413 Component m = target.getComponent(i);
414 if (m.isVisible()) {
415 Dimension d = m.getPreferredSize();
416 dim.height = Math.max(dim.height, d.height);
417 if (firstVisibleComponent) {
418 firstVisibleComponent = false;
419 } else {
420 dim.width += hgap;
421 }
422 dim.width += d.width;
423 if (useBaseline) {
424 int baseline = m.getBaseline(d.width, d.height);
425 if (baseline >= 0) {
426 maxAscent = Math.max(maxAscent, baseline);
427 maxDescent = Math.max(maxDescent, d.height
428 - baseline);
429 }
430 }
431 }
432 }
433 if (useBaseline) {
434 dim.height = Math.max(maxAscent + maxDescent,
435 dim.height);
436 }
437 Insets insets = target.getInsets();
438 dim.width += insets.left + insets.right + hgap * 2;
439 dim.height += insets.top + insets.bottom + vgap * 2;
440 return dim;
441 }
442 }
443
444 /**
445 * Returns the minimum dimensions needed to layout the <i>visible</i>
446 * components contained in the specified target container.
447 * @param target the container that needs to be laid out
448 * @return the minimum dimensions to lay out the
449 * subcomponents of the specified container
450 * @see #preferredLayoutSize
451 * @see java.awt.Container
452 * @see java.awt.Container#doLayout
453 */
454 public Dimension minimumLayoutSize(Container target) {
455 synchronized (target.getTreeLock()) {
456 boolean useBaseline = getAlignOnBaseline();
457 Dimension dim = new Dimension(0, 0);
458 int nmembers = target.getComponentCount();
459 int maxAscent = 0;
460 int maxDescent = 0;
461 boolean firstVisibleComponent = true;
462
463 for (int i = 0; i < nmembers; i++) {
464 Component m = target.getComponent(i);
465 if (m.visible) {
466 Dimension d = m.getMinimumSize();
467 dim.height = Math.max(dim.height, d.height);
468 if (firstVisibleComponent) {
469 firstVisibleComponent = false;
470 } else {
471 dim.width += hgap;
472 }
473 dim.width += d.width;
474 if (useBaseline) {
475 int baseline = m.getBaseline(d.width, d.height);
476 if (baseline >= 0) {
477 maxAscent = Math.max(maxAscent, baseline);
478 maxDescent = Math.max(maxDescent,
479 dim.height - baseline);
480 }
481 }
482 }
483 }
484
485 if (useBaseline) {
486 dim.height = Math.max(maxAscent + maxDescent,
487 dim.height);
488 }
489
490 Insets insets = target.getInsets();
491 dim.width += insets.left + insets.right + hgap * 2;
492 dim.height += insets.top + insets.bottom + vgap * 2;
493 return dim;
494
495 }
496 }
497
498 /**
499 * Centers the elements in the specified row, if there is any slack.
500 * @param target the component which needs to be moved
501 * @param x the x coordinate
502 * @param y the y coordinate
503 * @param width the width dimensions
504 * @param height the height dimensions
505 * @param rowStart the beginning of the row
506 * @param rowEnd the the ending of the row
507 * @param useBaseline Whether or not to align on baseline.
508 * @param ascent Ascent for the components. This is only valid if
509 * useBaseline is true.
510 * @param descent Ascent for the components. This is only valid if
511 * useBaseline is true.
512 * @return actual row height
513 */
514 private int moveComponents(Container target, int x, int y,
515 int width, int height, int rowStart, int rowEnd,
516 boolean ltr, boolean useBaseline, int[] ascent,
517 int[] descent) {
518 switch (newAlign) {
519 case LEFT:
520 x += ltr ? 0 : width;
521 break;
522 case CENTER:
523 x += width / 2;
524 break;
525 case RIGHT:
526 x += ltr ? width : 0;
527 break;
528 case LEADING:
529 break;
530 case TRAILING:
531 x += width;
532 break;
533 }
534 int maxAscent = 0;
535 int nonbaselineHeight = 0;
536 int baselineOffset = 0;
537 if (useBaseline) {
538 int maxDescent = 0;
539 for (int i = rowStart; i < rowEnd; i++) {
540 Component m = target.getComponent(i);
541 if (m.visible) {
542 if (ascent[i] >= 0) {
543 maxAscent = Math.max(maxAscent, ascent[i]);
544 maxDescent = Math.max(maxDescent, descent[i]);
545 } else {
546 nonbaselineHeight = Math.max(m.getHeight(),
547 nonbaselineHeight);
548 }
549 }
550 }
551 height = Math
552 .max(maxAscent + maxDescent, nonbaselineHeight);
553 baselineOffset = (height - maxAscent - maxDescent) / 2;
554 }
555 for (int i = rowStart; i < rowEnd; i++) {
556 Component m = target.getComponent(i);
557 if (m.isVisible()) {
558 int cy;
559 if (useBaseline && ascent[i] >= 0) {
560 cy = y + baselineOffset + maxAscent - ascent[i];
561 } else {
562 cy = y + (height - m.height) / 2;
563 }
564 if (ltr) {
565 m.setLocation(x, cy);
566 } else {
567 m.setLocation(target.width - x - m.width, cy);
568 }
569 x += m.width + hgap;
570 }
571 }
572 return height;
573 }
574
575 /**
576 * Lays out the container. This method lets each
577 * <i>visible</i> component take
578 * its preferred size by reshaping the components in the
579 * target container in order to satisfy the alignment of
580 * this <code>FlowLayout</code> object.
581 *
582 * @param target the specified component being laid out
583 * @see Container
584 * @see java.awt.Container#doLayout
585 */
586 public void layoutContainer(Container target) {
587 synchronized (target.getTreeLock()) {
588 Insets insets = target.getInsets();
589 int maxwidth = target.width
590 - (insets.left + insets.right + hgap * 2);
591 int nmembers = target.getComponentCount();
592 int x = 0, y = insets.top + vgap;
593 int rowh = 0, start = 0;
594
595 boolean ltr = target.getComponentOrientation()
596 .isLeftToRight();
597
598 boolean useBaseline = getAlignOnBaseline();
599 int[] ascent = null;
600 int[] descent = null;
601
602 if (useBaseline) {
603 ascent = new int[nmembers];
604 descent = new int[nmembers];
605 }
606
607 for (int i = 0; i < nmembers; i++) {
608 Component m = target.getComponent(i);
609 if (m.isVisible()) {
610 Dimension d = m.getPreferredSize();
611 m.setSize(d.width, d.height);
612
613 if (useBaseline) {
614 int baseline = m.getBaseline(d.width, d.height);
615 if (baseline >= 0) {
616 ascent[i] = baseline;
617 descent[i] = d.height - baseline;
618 } else {
619 ascent[i] = -1;
620 }
621 }
622 if ((x == 0) || ((x + d.width) <= maxwidth)) {
623 if (x > 0) {
624 x += hgap;
625 }
626 x += d.width;
627 rowh = Math.max(rowh, d.height);
628 } else {
629 rowh = moveComponents(target, insets.left
630 + hgap, y, maxwidth - x, rowh, start,
631 i, ltr, useBaseline, ascent, descent);
632 x = d.width;
633 y += vgap + rowh;
634 rowh = d.height;
635 start = i;
636 }
637 }
638 }
639 moveComponents(target, insets.left + hgap, y, maxwidth - x,
640 rowh, start, nmembers, ltr, useBaseline, ascent,
641 descent);
642 }
643 }
644
645 //
646 // the internal serial version which says which version was written
647 // - 0 (default) for versions before the Java 2 platform, v1.2
648 // - 1 for version >= Java 2 platform v1.2, which includes "newAlign" field
649 //
650 private static final int currentSerialVersion = 1;
651 /**
652 * This represent the <code>currentSerialVersion</code>
653 * which is bein used. It will be one of two values :
654 * <code>0</code> versions before Java 2 platform v1.2..
655 * <code>1</code> versions after Java 2 platform v1.2..
656 *
657 * @serial
658 * @since 1.2
659 */
660 private int serialVersionOnStream = currentSerialVersion;
661
662 /**
663 * Reads this object out of a serialization stream, handling
664 * objects written by older versions of the class that didn't contain all
665 * of the fields we use now..
666 */
667 private void readObject(ObjectInputStream stream)
668 throws IOException, ClassNotFoundException {
669 stream.defaultReadObject();
670
671 if (serialVersionOnStream < 1) {
672 // "newAlign" field wasn't present, so use the old "align" field.
673 setAlignment(this .align);
674 }
675 serialVersionOnStream = currentSerialVersion;
676 }
677
678 /**
679 * Returns a string representation of this <code>FlowLayout</code>
680 * object and its values.
681 * @return a string representation of this layout
682 */
683 public String toString() {
684 String str = "";
685 switch (align) {
686 case LEFT:
687 str = ",align=left";
688 break;
689 case CENTER:
690 str = ",align=center";
691 break;
692 case RIGHT:
693 str = ",align=right";
694 break;
695 case LEADING:
696 str = ",align=leading";
697 break;
698 case TRAILING:
699 str = ",align=trailing";
700 break;
701 }
702 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
703 + str + "]";
704 }
705
706 }
|