1 /*
2  * Copyright (c) 1995, 2019, 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}
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="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  */
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} 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      */
FlowLayout()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      */
FlowLayout(int align)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      */
FlowLayout(int align, int hgap, int vgap)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      */
getAlignment()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      */
setAlignment(int align)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      */
getHgap()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      */
setHgap(int hgap)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      */
getVgap()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      */
setVgap(int vgap)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      */
setAlignOnBaseline(boolean alignOnBaseline)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      */
getAlignOnBaseline()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      */
addLayoutComponent(String name, Component comp)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      */
removeLayoutComponent(Component comp)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      */
preferredLayoutSize(Container target)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      */
minimumLayoutSize(Container target)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      */
moveComponents(Container target, int x, int y, int width, int height, int rowStart, int rowEnd, boolean ltr, boolean useBaseline, int[] ascent, int[] descent)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      */
layoutContainer(Container target)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      */
readObject(ObjectInputStream stream)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      */
toString()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