View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package java.awt;
26  
27  import java.io.ObjectInputStream;
28  import java.io.IOException;
29  
30  /**
31   * A flow layout arranges components in a directional flow, much
32   * like lines of text in a paragraph. The flow direction is
33   * determined by the container's <code>componentOrientation</code>
34   * property and may be one of two values:
35   * <ul>
36   * <li><code>ComponentOrientation.LEFT_TO_RIGHT</code>
37   * <li><code>ComponentOrientation.RIGHT_TO_LEFT</code>
38   * </ul>
39   * Flow layouts are typically used
40   * to arrange buttons in a panel. It arranges buttons
41   * horizontally until no more buttons fit on the same line.
42   * The line alignment is determined by the <code>align</code>
43   * property. The possible values are:
44   * <ul>
45   * <li>{@link #LEFT LEFT}
46   * <li>{@link #RIGHT RIGHT}
47   * <li>{@link #CENTER CENTER}
48   * <li>{@link #LEADING LEADING}
49   * <li>{@link #TRAILING TRAILING}
50   * </ul>
51   * <p>
52   * For example, the following picture shows an applet using the flow
53   * layout manager (its default layout manager) to position three buttons:
54   * <p>
55   * <img src="doc-files/FlowLayout-1.gif"
56   * ALT="Graphic of Layout for Three Buttons"
57   * style="float:center; margin: 7px 10px;">
58   * <p>
59   * Here is the code for this applet:
60   *
61   * <hr><blockquote><pre>
62   * import java.awt.*;
63   * import java.applet.Applet;
64   *
65   * public class myButtons extends Applet {
66   *     Button button1, button2, button3;
67   *     public void init() {
68   *         button1 = new Button("Ok");
69   *         button2 = new Button("Open");
70   *         button3 = new Button("Close");
71   *         add(button1);
72   *         add(button2);
73   *         add(button3);
74   *     }
75   * }
76   * </pre></blockquote><hr>
77   * <p>
78   * A flow layout lets each component assume its natural (preferred) size.
79   *
80   * @author      Arthur van Hoff
81   * @author      Sami Shaio
82   * @since       JDK1.0
83   * @see ComponentOrientation
84   */
85  public class FlowLayout implements LayoutManager, java.io.Serializable {
86  
87      /**
88       * This value indicates that each row of components
89       * should be left-justified.
90       */
91      public static final int LEFT        = 0;
92  
93      /**
94       * This value indicates that each row of components
95       * should be centered.
96       */
97      public static final int CENTER      = 1;
98  
99      /**
100      * This value indicates that each row of components
101      * should be right-justified.
102      */
103     public static final int RIGHT       = 2;
104 
105     /**
106      * This value indicates that each row of components
107      * should be justified to the leading edge of the container's
108      * orientation, for example, to the left in left-to-right orientations.
109      *
110      * @see     java.awt.Component#getComponentOrientation
111      * @see     java.awt.ComponentOrientation
112      * @since   1.2
113      */
114     public static final int LEADING     = 3;
115 
116     /**
117      * This value indicates that each row of components
118      * should be justified to the trailing edge of the container's
119      * orientation, for example, to the right in left-to-right orientations.
120      *
121      * @see     java.awt.Component#getComponentOrientation
122      * @see     java.awt.ComponentOrientation
123      * @since   1.2
124      */
125     public static final int TRAILING = 4;
126 
127     /**
128      * <code>align</code> is the property that determines
129      * how each row distributes empty space.
130      * It can be one of the following values:
131      * <ul>
132      * <li><code>LEFT</code>
133      * <li><code>RIGHT</code>
134      * <li><code>CENTER</code>
135      * </ul>
136      *
137      * @serial
138      * @see #getAlignment
139      * @see #setAlignment
140      */
141     int align;          // This is for 1.1 serialization compatibility
142 
143     /**
144      * <code>newAlign</code> is the property that determines
145      * how each row distributes empty space for the Java 2 platform,
146      * v1.2 and greater.
147      * It can be one of the following three values:
148      * <ul>
149      * <li><code>LEFT</code>
150      * <li><code>RIGHT</code>
151      * <li><code>CENTER</code>
152      * <li><code>LEADING</code>
153      * <li><code>TRAILING</code>
154      * </ul>
155      *
156      * @serial
157      * @since 1.2
158      * @see #getAlignment
159      * @see #setAlignment
160      */
161     int newAlign;       // This is the one we actually use
162 
163     /**
164      * The flow layout manager allows a seperation of
165      * components with gaps.  The horizontal gap will
166      * specify the space between components and between
167      * the components and the borders of the
168      * <code>Container</code>.
169      *
170      * @serial
171      * @see #getHgap()
172      * @see #setHgap(int)
173      */
174     int hgap;
175 
176     /**
177      * The flow layout manager allows a seperation of
178      * components with gaps.  The vertical gap will
179      * specify the space between rows and between the
180      * the rows and the borders of the <code>Container</code>.
181      *
182      * @serial
183      * @see #getHgap()
184      * @see #setHgap(int)
185      */
186     int vgap;
187 
188     /**
189      * If true, components will be aligned on their baseline.
190      */
191     private boolean alignOnBaseline;
192 
193     /*
194      * JDK 1.1 serialVersionUID
195      */
196      private static final long serialVersionUID = -7262534875583282631L;
197 
198     /**
199      * Constructs a new <code>FlowLayout</code> with a centered alignment and a
200      * default 5-unit horizontal and vertical gap.
201      */
202     public FlowLayout() {
203         this(CENTER, 5, 5);
204     }
205 
206     /**
207      * Constructs a new <code>FlowLayout</code> with the specified
208      * alignment and a default 5-unit horizontal and vertical gap.
209      * The value of the alignment argument must be one of
210      * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
211      * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>,
212      * or <code>FlowLayout.TRAILING</code>.
213      * @param align the alignment value
214      */
215     public FlowLayout(int align) {
216         this(align, 5, 5);
217     }
218 
219     /**
220      * Creates a new flow layout manager with the indicated alignment
221      * and the indicated horizontal and vertical gaps.
222      * <p>
223      * The value of the alignment argument must be one of
224      * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
225      * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>,
226      * or <code>FlowLayout.TRAILING</code>.
227      * @param      align   the alignment value
228      * @param      hgap    the horizontal gap between components
229      *                     and between the components and the
230      *                     borders of the <code>Container</code>
231      * @param      vgap    the vertical gap between components
232      *                     and between the components and the
233      *                     borders of the <code>Container</code>
234      */
235     public FlowLayout(int align, int hgap, int vgap) {
236         this.hgap = hgap;
237         this.vgap = vgap;
238         setAlignment(align);
239     }
240 
241     /**
242      * Gets the alignment for this layout.
243      * Possible values are <code>FlowLayout.LEFT</code>,
244      * <code>FlowLayout.RIGHT</code>, <code>FlowLayout.CENTER</code>,
245      * <code>FlowLayout.LEADING</code>,
246      * or <code>FlowLayout.TRAILING</code>.
247      * @return     the alignment value for this layout
248      * @see        java.awt.FlowLayout#setAlignment
249      * @since      JDK1.1
250      */
251     public int getAlignment() {
252         return newAlign;
253     }
254 
255     /**
256      * Sets the alignment for this layout.
257      * Possible values are
258      * <ul>
259      * <li><code>FlowLayout.LEFT</code>
260      * <li><code>FlowLayout.RIGHT</code>
261      * <li><code>FlowLayout.CENTER</code>
262      * <li><code>FlowLayout.LEADING</code>
263      * <li><code>FlowLayout.TRAILING</code>
264      * </ul>
265      * @param      align one of the alignment values shown above
266      * @see        #getAlignment()
267      * @since      JDK1.1
268      */
269     public void setAlignment(int align) {
270         this.newAlign = align;
271 
272         // this.align is used only for serialization compatibility,
273         // so set it to a value compatible with the 1.1 version
274         // of the class
275 
276         switch (align) {
277         case LEADING:
278             this.align = LEFT;
279             break;
280         case TRAILING:
281             this.align = RIGHT;
282             break;
283         default:
284             this.align = align;
285             break;
286         }
287     }
288 
289     /**
290      * Gets the horizontal gap between components
291      * and between the components and the borders
292      * of the <code>Container</code>
293      *
294      * @return     the horizontal gap between components
295      *             and between the components and the borders
296      *             of the <code>Container</code>
297      * @see        java.awt.FlowLayout#setHgap
298      * @since      JDK1.1
299      */
300     public int getHgap() {
301         return hgap;
302     }
303 
304     /**
305      * Sets the horizontal gap between components and
306      * between the components and the borders of the
307      * <code>Container</code>.
308      *
309      * @param hgap the horizontal gap between components
310      *             and between the components and the borders
311      *             of the <code>Container</code>
312      * @see        java.awt.FlowLayout#getHgap
313      * @since      JDK1.1
314      */
315     public void setHgap(int hgap) {
316         this.hgap = hgap;
317     }
318 
319     /**
320      * Gets the vertical gap between components and
321      * between the components and the borders of the
322      * <code>Container</code>.
323      *
324      * @return     the vertical gap between components
325      *             and between the components and the borders
326      *             of the <code>Container</code>
327      * @see        java.awt.FlowLayout#setVgap
328      * @since      JDK1.1
329      */
330     public int getVgap() {
331         return vgap;
332     }
333 
334     /**
335      * Sets the vertical gap between components and between
336      * the components and the borders of the <code>Container</code>.
337      *
338      * @param vgap the vertical gap between components
339      *             and between the components and the borders
340      *             of the <code>Container</code>
341      * @see        java.awt.FlowLayout#getVgap
342      * @since      JDK1.1
343      */
344     public void setVgap(int vgap) {
345         this.vgap = vgap;
346     }
347 
348     /**
349      * Sets whether or not components should be vertically aligned along their
350      * baseline.  Components that do not have a baseline will be centered.
351      * The default is false.
352      *
353      * @param alignOnBaseline whether or not components should be
354      *                        vertically aligned on their baseline
355      * @since 1.6
356      */
357     public void setAlignOnBaseline(boolean alignOnBaseline) {
358         this.alignOnBaseline = alignOnBaseline;
359     }
360 
361     /**
362      * Returns true if components are to be vertically aligned along
363      * their baseline.  The default is false.
364      *
365      * @return true if components are to be vertically aligned along
366      *              their baseline
367      * @since 1.6
368      */
369     public boolean getAlignOnBaseline() {
370         return alignOnBaseline;
371     }
372 
373     /**
374      * Adds the specified component to the layout.
375      * Not used by this class.
376      * @param name the name of the component
377      * @param comp the component to be added
378      */
379     public void addLayoutComponent(String name, Component comp) {
380     }
381 
382     /**
383      * Removes the specified component from the layout.
384      * Not used by this class.
385      * @param comp the component to remove
386      * @see       java.awt.Container#removeAll
387      */
388     public void removeLayoutComponent(Component comp) {
389     }
390 
391     /**
392      * Returns the preferred dimensions for this layout given the
393      * <i>visible</i> components in the specified target container.
394      *
395      * @param target the container that needs to be laid out
396      * @return    the preferred dimensions to lay out the
397      *            subcomponents of the specified container
398      * @see Container
399      * @see #minimumLayoutSize
400      * @see       java.awt.Container#getPreferredSize
401      */
402     public Dimension preferredLayoutSize(Container target) {
403       synchronized (target.getTreeLock()) {
404         Dimension dim = new Dimension(0, 0);
405         int nmembers = target.getComponentCount();
406         boolean firstVisibleComponent = true;
407         boolean useBaseline = getAlignOnBaseline();
408         int maxAscent = 0;
409         int maxDescent = 0;
410 
411         for (int i = 0 ; i < nmembers ; i++) {
412             Component m = target.getComponent(i);
413             if (m.isVisible()) {
414                 Dimension d = m.getPreferredSize();
415                 dim.height = Math.max(dim.height, d.height);
416                 if (firstVisibleComponent) {
417                     firstVisibleComponent = false;
418                 } else {
419                     dim.width += hgap;
420                 }
421                 dim.width += d.width;
422                 if (useBaseline) {
423                     int baseline = m.getBaseline(d.width, d.height);
424                     if (baseline >= 0) {
425                         maxAscent = Math.max(maxAscent, baseline);
426                         maxDescent = Math.max(maxDescent, d.height - baseline);
427                     }
428                 }
429             }
430         }
431         if (useBaseline) {
432             dim.height = Math.max(maxAscent + maxDescent, dim.height);
433         }
434         Insets insets = target.getInsets();
435         dim.width += insets.left + insets.right + hgap*2;
436         dim.height += insets.top + insets.bottom + vgap*2;
437         return dim;
438       }
439     }
440 
441     /**
442      * Returns the minimum dimensions needed to layout the <i>visible</i>
443      * components contained in the specified target container.
444      * @param target the container that needs to be laid out
445      * @return    the minimum dimensions to lay out the
446      *            subcomponents of the specified container
447      * @see #preferredLayoutSize
448      * @see       java.awt.Container
449      * @see       java.awt.Container#doLayout
450      */
451     public Dimension minimumLayoutSize(Container target) {
452       synchronized (target.getTreeLock()) {
453         boolean useBaseline = getAlignOnBaseline();
454         Dimension dim = new Dimension(0, 0);
455         int nmembers = target.getComponentCount();
456         int maxAscent = 0;
457         int maxDescent = 0;
458         boolean firstVisibleComponent = true;
459 
460         for (int i = 0 ; i < nmembers ; i++) {
461             Component m = target.getComponent(i);
462             if (m.visible) {
463                 Dimension d = m.getMinimumSize();
464                 dim.height = Math.max(dim.height, d.height);
465                 if (firstVisibleComponent) {
466                     firstVisibleComponent = false;
467                 } else {
468                     dim.width += hgap;
469                 }
470                 dim.width += d.width;
471                 if (useBaseline) {
472                     int baseline = m.getBaseline(d.width, d.height);
473                     if (baseline >= 0) {
474                         maxAscent = Math.max(maxAscent, baseline);
475                         maxDescent = Math.max(maxDescent,
476                                               dim.height - baseline);
477                     }
478                 }
479 }
480 }
481 
482         if (useBaseline) {
483             dim.height = Math.max(maxAscent + maxDescent, dim.height);
484         }
485 
486         Insets insets = target.getInsets();
487         dim.width += insets.left + insets.right + hgap*2;
488         dim.height += insets.top + insets.bottom + vgap*2;
489         return dim;
490 
491 
492 
493 
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, int width, int height,
515                                 int rowStart, int rowEnd, boolean ltr,
516                                 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                     }
546                     else {
547                         nonbaselineHeight = Math.max(m.getHeight(),
548                                                      nonbaselineHeight);
549                     }
550                 }
551             }
552             height = Math.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                 }
562                 else {
563                     cy = y + (height - m.height) / 2;
564                 }
565                 if (ltr) {
566                     m.setLocation(x, cy);
567                 } else {
568                     m.setLocation(target.width - x - m.width, cy);
569                 }
570                 x += m.width + hgap;
571             }
572         }
573         return height;
574     }
575 
576     /**
577      * Lays out the container. This method lets each
578      * <i>visible</i> component take
579      * its preferred size by reshaping the components in the
580      * target container in order to satisfy the alignment of
581      * this <code>FlowLayout</code> object.
582      *
583      * @param target the specified component being laid out
584      * @see Container
585      * @see       java.awt.Container#doLayout
586      */
587     public void layoutContainer(Container target) {
588       synchronized (target.getTreeLock()) {
589         Insets insets = target.getInsets();
590         int maxwidth = target.width - (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().isLeftToRight();
596 
597         boolean useBaseline = getAlignOnBaseline();
598         int[] ascent = null;
599         int[] descent = null;
600 
601         if (useBaseline) {
602             ascent = new int[nmembers];
603             descent = new int[nmembers];
604         }
605 
606         for (int i = 0 ; i < nmembers ; i++) {
607             Component m = target.getComponent(i);
608             if (m.isVisible()) {
609                 Dimension d = m.getPreferredSize();
610                 m.setSize(d.width, d.height);
611 
612                 if (useBaseline) {
613                     int baseline = m.getBaseline(d.width, d.height);
614                     if (baseline >= 0) {
615                         ascent[i] = baseline;
616                         descent[i] = d.height - baseline;
617                     }
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 + hgap, y,
630                                    maxwidth - x, rowh, start, i, ltr,
631                                    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, rowh,
640                        start, nmembers, ltr, useBaseline, ascent, descent);
641       }
642     }
643 
644     //
645     // the internal serial version which says which version was written
646     // - 0 (default) for versions before the Java 2 platform, v1.2
647     // - 1 for version >= Java 2 platform v1.2, which includes "newAlign" field
648     //
649     private static final int currentSerialVersion = 1;
650     /**
651      * This represent the <code>currentSerialVersion</code>
652      * which is bein used.  It will be one of two values :
653      * <code>0</code> versions before Java 2 platform v1.2..
654      * <code>1</code> versions after  Java 2 platform v1.2..
655      *
656      * @serial
657      * @since 1.2
658      */
659     private int serialVersionOnStream = currentSerialVersion;
660 
661     /**
662      * Reads this object out of a serialization stream, handling
663      * objects written by older versions of the class that didn't contain all
664      * of the fields we use now..
665      */
666     private void readObject(ObjectInputStream stream)
667          throws IOException, ClassNotFoundException
668     {
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:        str = ",align=left"; break;
687           case CENTER:      str = ",align=center"; break;
688           case RIGHT:       str = ",align=right"; break;
689           case LEADING:     str = ",align=leading"; break;
690           case TRAILING:    str = ",align=trailing"; break;
691         }
692         return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
693     }
694 
695 
696 }