001/*
002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
003 * 
004 * http://www.izforge.com/izpack/
005 * http://developer.berlios.de/projects/izpack/
006 * 
007 * Copyright 2002 Elmar Grom
008 * 
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *     http://www.apache.org/licenses/LICENSE-2.0
014 *     
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 */
021
022package com.izforge.izpack.gui;
023
024import java.awt.Component;
025import java.awt.Container;
026import java.awt.Dimension;
027import java.awt.Insets;
028import java.awt.LayoutManager;
029
030/*---------------------------------------------------------------------------*/
031/**
032 * A flow layout arranges components in a left-to-right flow, much like lines of text in a
033 * paragraph. Flow layouts are typically used to arrange buttons in a panel. It will arrange buttons
034 * left to right until no more buttons fit on the same line. Each line is centered.
035 * <p>
036 * For example, the following picture shows an applet using the flow layout manager (its default
037 * layout manager) to position three buttons:
038 * <p>
039 * <img src="doc-files/FlowLayout-1.gif" ALT="Graphic of Layout for Three Buttons" ALIGN=center
040 * HSPACE=10 VSPACE=7>
041 * <p>
042 * Here is the code for this applet:
043 * <p>
044 * <hr>
045 * <blockquote>
046 * 
047 * <pre>
048 * import java.awt.*;
049 * import java.applet.Applet;
050 * 
051 * public class myButtons extends Applet
052 * {
053 * 
054 *     Button button1, button2, button3;
055 * 
056 *     public void init()
057 *     {
058 *         button1 = new Button(&quot;Ok&quot;);
059 *         button2 = new Button(&quot;Open&quot;);
060 *         button3 = new Button(&quot;Close&quot;);
061 *         add(button1);
062 *         add(button2);
063 *         add(button3);
064 *     }
065 * }
066 * 
067 * </pre>
068 * 
069 * </blockquote>
070 * <hr>
071 * <p>
072 * A flow layout lets each component assume its natural (preferred) size.
073 * 
074 * This class is a bit different from java.awt.FlowLayout. <blockquote> java.awt.FlowLayout has a
075 * minor problem that was bugging me when I wrote the UserInputPanel. FlowLayout puts some amount of
076 * space in between each component that it lays out. In addition it adds that same amount of space
077 * to the left and to the right of the entire group. Therefore items such as the RuleInputfield that
078 * are laid out with a FlowLayout would never line up properly with the other components (it would
079 * appear to be slightly indented). Because there is no way to circumvent this behavior in
080 * FlowLayout (it's hard coded) I copied the source and modified it so that it does not add the
081 * space to the left and to the right. Now my stuff lines up properly. (Elmar Grom)</blockquote>
082 * 
083 * @version 1.39, 11/29/02
084 * @author Arthur van Hoff
085 * @author Sami Shaio
086 * @author Elmar Grom
087 */
088/*---------------------------------------------------------------------------*/
089public class FlowLayout implements LayoutManager
090{
091
092    /**
093     * This value indicates that each row of components should be left-justified.
094     */
095    public static final int LEFT = 0;
096
097    /**
098     * This value indicates that each row of components should be centered.
099     */
100    public static final int CENTER = 1;
101
102    /**
103     * This value indicates that each row of components should be right-justified.
104     */
105    public static final int RIGHT = 2;
106
107    /**
108     * This value indicates that each row of components should be justified to the leading edge of
109     * the container's 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 Package-private pending API change approval
114     */
115    public static final int LEADING = 3;
116
117    /**
118     * This value indicates that each row of components should be justified to the leading edge of
119     * the container's 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 Package-private pending API change approval
124     */
125    public static final int TRAILING = 4;
126
127    /**
128     * <code>align</code> is the proprty that determines how each row distributes empty space. It
129     * can be one of the following three values : <code>LEFT</code>
130     * <code>RIGHT</code>
131     * <code>CENTER</code>
132     * 
133     * @serial
134     * @see #getAlignment
135     * @see #setAlignment
136     */
137    int align; // This is for 1.1 serialization compatibilitys
138
139    /**
140     * <code>newAlign</code> is the property that determines how each row distributes empty space
141     * for the Java 2 platform, v1.2 and greater. It can be one of the following three values :
142     * <code>LEFT</code>
143     * <code>RIGHT</code>
144     * <code>CENTER</code>
145     * 
146     * @serial
147     * @since 1.2
148     * @see #getAlignment
149     * @see #setAlignment
150     */
151    int newAlign; // This is the one we actually use
152
153    /**
154     * The flow layout manager allows a seperation of components with gaps. The horizontal gap will
155     * specify the space between components.
156     * 
157     * @serial
158     * @see #getHgap
159     * @see #setHgap
160     */
161    int hgap;
162
163    /**
164     * The flow layout manager allows a seperation of components with gaps. The vertical gap will
165     * specify the space between rows.
166     * 
167     * @serial
168     * @see #getVgap
169     * @see #setVgap
170     */
171    int vgap;
172
173    /*--------------------------------------------------------------------------*/
174    /**
175     * Constructs a new Flow Layout with a centered alignment and a default 5-unit horizontal and
176     * vertical gap.
177     */
178    /*--------------------------------------------------------------------------*/
179    public FlowLayout()
180    {
181        this(CENTER, 5, 5);
182    }
183
184    /*--------------------------------------------------------------------------*/
185    /**
186     * Constructs a new Flow Layout with the specified alignment and a default 5-unit horizontal and
187     * vertical gap. The value of the alignment argument must be one of <code>FlowLayout.LEFT</code>,
188     * <code>FlowLayout.RIGHT</code>, or <code>FlowLayout.CENTER</code>.
189     * 
190     * @param align the alignment value
191     */
192    /*--------------------------------------------------------------------------*/
193    public FlowLayout(int align)
194    {
195        this(align, 5, 5);
196    }
197
198    /*--------------------------------------------------------------------------*/
199    /**
200     * Creates a new flow layout manager with the indicated alignment and the indicated horizontal
201     * and vertical gaps.
202     * <p>
203     * The value of the alignment argument must be one of <code>FlowLayout.LEFT</code>,
204     * <code>FlowLayout.RIGHT</code>, or <code>FlowLayout.CENTER</code>.
205     * 
206     * @param align the alignment value.
207     * @param hgap the horizontal gap between components.
208     * @param vgap the vertical gap between components.
209     */
210    /*--------------------------------------------------------------------------*/
211    public FlowLayout(int align, int hgap, int vgap)
212    {
213        this.hgap = hgap;
214        this.vgap = vgap;
215        setAlignment(align);
216    }
217
218    /*--------------------------------------------------------------------------*/
219    /**
220     * Gets the alignment for this layout. Possible values are <code>FlowLayout.LEFT</code>,
221     * <code>FlowLayout.RIGHT</code>, or <code>FlowLayout.CENTER</code>.
222     * 
223     * @return the alignment value for this layout.
224     * 
225     * @see java.awt.FlowLayout#setAlignment
226     */
227    /*--------------------------------------------------------------------------*/
228    public int getAlignment()
229    {
230        return (newAlign);
231    }
232
233    /*--------------------------------------------------------------------------*/
234    /**
235     * Sets the alignment for this layout. Possible values are <code>FlowLayout.LEFT</code>,
236     * <code>FlowLayout.RIGHT</code>, and <code>FlowLayout.CENTER</code>.
237     * 
238     * @param align the alignment value.
239     * 
240     * @see #getAlignment()
241     */
242    /*--------------------------------------------------------------------------*/
243    public void setAlignment(int align)
244    {
245        this.newAlign = align;
246
247        // this.align is used only for serialization compatibility,
248        // so set it to a value compatible with the 1.1 version
249        // of the class
250
251        switch (align)
252        {
253        case LEADING:
254            this.align = LEFT;
255            break;
256        case TRAILING:
257            this.align = RIGHT;
258            break;
259        default:
260            this.align = align;
261            break;
262        }
263    }
264
265    /*--------------------------------------------------------------------------*/
266    /**
267     * Gets the horizontal gap between components.
268     * 
269     * @return the horizontal gap between components.
270     * 
271     * @see #setHgap(int)
272     */
273    /*--------------------------------------------------------------------------*/
274    public int getHgap()
275    {
276        return (hgap);
277    }
278
279    /*--------------------------------------------------------------------------*/
280    /**
281     * Sets the horizontal gap between components.
282     * 
283     * @param hgap the horizontal gap between components
284     * 
285     * @see #getHgap()
286     */
287    /*--------------------------------------------------------------------------*/
288    public void setHgap(int hgap)
289    {
290        this.hgap = hgap;
291    }
292
293    /*--------------------------------------------------------------------------*/
294    /**
295     * Gets the vertical gap between components.
296     * 
297     * @return the vertical gap between components.\
298     * 
299     * @see #setVgap(int)
300     */
301    /*--------------------------------------------------------------------------*/
302    public int getVgap()
303    {
304        return (vgap);
305    }
306
307    /*--------------------------------------------------------------------------*/
308    /**
309     * Sets the vertical gap between components.
310     * 
311     * @param vgap the vertical gap between components
312     * 
313     * @see #getVgap()
314     */
315    /*--------------------------------------------------------------------------*/
316    public void setVgap(int vgap)
317    {
318        this.vgap = vgap;
319    }
320
321    /*--------------------------------------------------------------------------*/
322    /**
323     * Adds the specified component to the layout. Not used by this class.
324     * 
325     * @param name the name of the component
326     * @param comp the component to be added
327     */
328    /*--------------------------------------------------------------------------*/
329    public void addLayoutComponent(String name, Component comp)
330    {
331    }
332
333    /*--------------------------------------------------------------------------*/
334    /**
335     * Removes the specified component from the layout. Not used by this class.
336     * 
337     * @param comp the component to remove
338     * 
339     */
340    /*--------------------------------------------------------------------------*/
341    public void removeLayoutComponent(Component comp)
342    {
343    }
344
345    /*--------------------------------------------------------------------------*/
346    /**
347     * Returns the preferred dimensions for this layout given the components in the specified target
348     * container.
349     * 
350     * @param target the component which needs to be laid out
351     * 
352     * @return the preferred dimensions to lay out the subcomponents of the specified container.
353     * @see #minimumLayoutSize(Container)
354     */
355    /*--------------------------------------------------------------------------*/
356    public Dimension preferredLayoutSize(Container target)
357    {
358        synchronized (target.getTreeLock())
359        {
360            Dimension dim = new Dimension(0, 0);
361            int nmembers = target.getComponentCount();
362            boolean firstVisibleComponent = true;
363
364            for (int i = 0; i < nmembers; i++)
365            {
366                Component m = target.getComponent(i);
367                if (m.isVisible())
368                {
369                    Dimension d = m.getPreferredSize();
370                    dim.height = Math.max(dim.height, d.height);
371                    if (firstVisibleComponent)
372                    {
373                        firstVisibleComponent = false;
374                    }
375                    else
376                    {
377                        dim.width += hgap;
378                    }
379                    dim.width += d.width;
380                }
381            }
382
383            Insets insets = target.getInsets();
384            dim.width += insets.left + insets.right + hgap * 2;
385            dim.height += insets.top + insets.bottom + vgap * 2;
386
387            return (dim);
388        }
389    }
390
391    /*--------------------------------------------------------------------------*/
392    /**
393     * Returns the minimum dimensions needed to layout the components contained in the specified
394     * target container.
395     * 
396     * @param target the component which needs to be laid out
397     * 
398     * @return the minimum dimensions to lay out the subcomponents of the specified container.
399     * 
400     * @see #preferredLayoutSize(Container)
401     */
402    /*--------------------------------------------------------------------------*/
403    public Dimension minimumLayoutSize(Container target)
404    {
405        synchronized (target.getTreeLock())
406        {
407            Dimension dim = new Dimension(0, 0);
408            int nmembers = target.getComponentCount();
409
410            for (int i = 0; i < nmembers; i++)
411            {
412                Component m = target.getComponent(i);
413                if (m.isVisible())
414                {
415                    Dimension d = m.getMinimumSize();
416                    dim.height = Math.max(dim.height, d.height);
417                    if (i > 0)
418                    {
419                        dim.width += hgap;
420                    }
421                    dim.width += d.width;
422                }
423            }
424
425            Insets insets = target.getInsets();
426            dim.width += insets.left + insets.right + hgap * 2;
427            dim.height += insets.top + insets.bottom + vgap * 2;
428
429            return (dim);
430        }
431    }
432
433    /*--------------------------------------------------------------------------*/
434    /**
435     * Centers the elements in the specified row, if there is any slack.
436     * 
437     * @param target the component which needs to be moved
438     * @param x the x coordinate
439     * @param y the y coordinate
440     * @param width the width dimensions
441     * @param height the height dimensions
442     * @param rowStart the beginning of the row
443     * @param rowEnd the the ending of the row
444     */
445    /*--------------------------------------------------------------------------*/
446    private void moveComponents(Container target, int x, int y, int width, int height,
447            int rowStart, int rowEnd, boolean ltr)
448    {
449        synchronized (target.getTreeLock())
450        {
451            switch (newAlign)
452            {
453            case LEFT:
454                x += ltr ? 0 : width;
455                break;
456            case CENTER:
457                x += width / 2;
458                break;
459            case RIGHT:
460                x += ltr ? width : 0;
461                break;
462            case LEADING:
463                break;
464            case TRAILING:
465                x += width;
466                break;
467            }
468
469            for (int i = rowStart; i < rowEnd; i++)
470            {
471                Component m = target.getComponent(i);
472
473                if (m.isVisible())
474                {
475                    if (ltr)
476                    {
477                        m.setLocation(x, y + (height - m.getSize().height) / 2);
478                    }
479                    else
480                    {
481                        m.setLocation(target.getSize().width - x - m.getSize().width, y
482                                + (height - m.getSize().height) / 2);
483                    }
484
485                    x += m.getSize().width + hgap;
486                }
487            }
488        }
489    }
490
491    /*--------------------------------------------------------------------------*/
492    /**
493     * Lays out the container. This method lets each component take its preferred size by reshaping
494     * the components in the target container in order to satisfy the constraints of this
495     * <code>FlowLayout</code> object.
496     * 
497     * @param target the specified component being laid out.
498     * 
499     */
500    /*--------------------------------------------------------------------------*/
501    public void layoutContainer(Container target)
502    {
503        synchronized (target.getTreeLock())
504        {
505            Insets insets = target.getInsets();
506            int maxWidth = target.getSize().width - (insets.left + insets.right + hgap * 2);
507            int nMembers = target.getComponentCount();
508            int x = 0;
509            int y = insets.top + vgap;
510            int rowh = 0;
511            int start = 0;
512
513            boolean ltr = target.getComponentOrientation().isLeftToRight();
514
515            for (int i = 0; i < nMembers; i++)
516            {
517                Component m = target.getComponent(i);
518
519                if (m.isVisible())
520                {
521                    Dimension d = m.getPreferredSize();
522                    m.setSize(d.width, d.height);
523
524                    if ((x == 0) || ((x + d.width) <= maxWidth))
525                    {
526                        if (x > 0)
527                        {
528                            x += hgap;
529                        }
530                        x += d.width;
531                        rowh = Math.max(rowh, d.height);
532                    }
533                    else
534                    {
535                        moveComponents(target, insets.left, y, maxWidth - x, rowh, start, i, ltr);
536                        x = d.width;
537                        y += vgap + rowh;
538                        rowh = d.height;
539                        start = i;
540                    }
541                }
542            }
543
544            moveComponents(target, insets.left, y, maxWidth - x, rowh, start, nMembers, ltr);
545        }
546    }
547
548    /*--------------------------------------------------------------------------*/
549    /**
550     * Returns a string representation of this <code>FlowLayout</code> object and its values.
551     * 
552     * @return a string representation of this layout.
553     */
554    /*--------------------------------------------------------------------------*/
555    public String toString()
556    {
557        String str = "";
558
559        switch (align)
560        {
561        case LEFT:
562            str = ",align=left";
563            break;
564        case CENTER:
565            str = ",align=center";
566            break;
567        case RIGHT:
568            str = ",align=right";
569            break;
570        case LEADING:
571            str = ",align=leading";
572            break;
573        case TRAILING:
574            str = ",align=trailing";
575            break;
576        }
577
578        return (getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]");
579    }
580}
581/*---------------------------------------------------------------------------*/