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 */
25package java.awt;
26
27import java.io.ObjectInputStream;
28import 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}
34 * property and may be one of two values:
35 * <ul>
36 * <li>{@code ComponentOrientation.LEFT_TO_RIGHT}
37 * <li>{@code ComponentOrientation.RIGHT_TO_LEFT}
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}
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       1.0
83 * @see ComponentOrientation
84 */
85public 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} 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}
133     * <li>{@code RIGHT}
134     * <li>{@code CENTER}
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} 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}
150     * <li>{@code RIGHT}
151     * <li>{@code CENTER}
152     * <li>{@code LEADING}
153     * <li>{@code TRAILING}
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 separation 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}.
169     *
170     * @serial
171     * @see #getHgap()
172     * @see #setHgap(int)
173     */
174    int hgap;
175
176    /**
177     * The flow layout manager allows a separation 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}.
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} 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} 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 FlowLayout.RIGHT},
211     * {@code FlowLayout.CENTER}, {@code FlowLayout.LEADING},
212     * or {@code FlowLayout.TRAILING}.
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 FlowLayout.RIGHT},
225     * {@code FlowLayout.CENTER}, {@code FlowLayout.LEADING},
226     * or {@code FlowLayout.TRAILING}.
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}
231     * @param      vgap    the vertical gap between components
232     *                     and between the components and the
233     *                     borders of the {@code Container}
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},
244     * {@code FlowLayout.RIGHT}, {@code FlowLayout.CENTER},
245     * {@code FlowLayout.LEADING},
246     * or {@code FlowLayout.TRAILING}.
247     * @return     the alignment value for this layout
248     * @see        java.awt.FlowLayout#setAlignment
249     * @since      1.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}
260     * <li>{@code FlowLayout.RIGHT}
261     * <li>{@code FlowLayout.CENTER}
262     * <li>{@code FlowLayout.LEADING}
263     * <li>{@code FlowLayout.TRAILING}
264     * </ul>
265     * @param      align one of the alignment values shown above
266     * @see        #getAlignment()
267     * @since      1.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}
293     *
294     * @return     the horizontal gap between components
295     *             and between the components and the borders
296     *             of the {@code Container}
297     * @see        java.awt.FlowLayout#setHgap
298     * @since      1.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}.
308     *
309     * @param hgap the horizontal gap between components
310     *             and between the components and the borders
311     *             of the {@code Container}
312     * @see        java.awt.FlowLayout#getHgap
313     * @since      1.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}.
323     *
324     * @return     the vertical gap between components
325     *             and between the components and the borders
326     *             of the {@code Container}
327     * @see        java.awt.FlowLayout#setVgap
328     * @since      1.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}.
337     *
338     * @param vgap the vertical gap between components
339     *             and between the components and the borders
340     *             of the {@code Container}
341     * @see        java.awt.FlowLayout#getVgap
342     * @since      1.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 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} 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}
652     * which is bein used.  It will be one of two values:
653     * {@code 0} versions before Java 2 platform v1.2,
654     * {@code 1} 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}
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}
697