001/* FlowLayout.java -- Grid-based layout engine
002   Copyright (C) 1999, 2000, 2001, 2002, 2004  Free Software Foundation
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt;
040
041import java.io.Serializable;
042
043/** This class implements a flow-based layout.  Components are laid
044 * out in order from left to right.  When a component cannot be placed
045 * without horizontal clipping, a new row is started.  This class
046 * supports horizontal and vertical gaps.  These are used for spacing
047 * between components.
048 *
049 * @author Tom Tromey (tromey@redhat.com)
050 * @author Aaron M. Renn (arenn@urbanophile.com)
051 */
052public class FlowLayout implements LayoutManager, Serializable
053{
054  /** Constant that specifies left alignment.  */
055  public static final int LEFT = 0;
056  /** Constant that specifies center alignment.  */
057  public static final int CENTER = 1;
058  /** Constant that specifies right alignment.  */
059  public static final int RIGHT = 2;
060
061  /** Constant that specifies alignment to leading edge of container's
062   * orientation.  */
063  public static final int LEADING = 3;
064  /** Constant that specifies alignment to trailing edge of container's
065   * orientation.  */
066  public static final int TRAILING = 4;
067
068  // Serialization constant
069  private static final long serialVersionUID = -7262534875583282631L;
070
071  /**
072   * Add a new component to the layout.  This particular implementation
073   * does nothing.
074   *
075   * @param name the name
076   * @param comp the component 
077   */
078  public void addLayoutComponent (String name, Component comp)
079  {
080    // Nothing.
081  }
082
083  /**
084   * Returns the current justification value for this object.
085   *
086   * @return The current justification value for this object.
087   */
088  public int getAlignment ()
089  {
090    return align;
091  }
092
093  /**
094   * Returns the horizontal gap between components.
095   *
096   * @return The horizontal gap between components.
097   */
098  public int getHgap ()
099  {
100    return hgap;
101  }
102
103  /**
104   * Returns the vertical gap between lines of components.
105   *
106   * @return The vertical gap between lines of components.
107   */
108  public int getVgap ()
109  {
110    return vgap;
111  }
112
113  /**
114   * Initializes a new instance of <code>FlowLayout</code> with a center
115   * justification and a default horizontal and vertical gap of 5.
116   */
117  public FlowLayout ()
118  {
119    this (CENTER, 5, 5);
120  }
121
122  /**
123   * Initializes a new instance of <code>FlowLayout</code> with the specified
124   * justification and a default horizontal and vertical gap of 5.
125   *
126   * @param align The justification setting, which should be one of the
127   * contants in this class.
128   */
129  public FlowLayout (int align)
130  {
131    this (align, 5, 5);
132  }
133
134  /**
135   * Initializes a new instance of <code>FlowLayout</code> with the specified
136   * justification and gap values
137   * @param align Alignment
138   * @param hgap The horizontal gap
139   * @param vgap The vertical gap
140   * @exception IllegalArgumentException If either gap is negative
141   */
142  public FlowLayout (int align, int hgap, int vgap)
143  {
144    // Use methods to set fields so that we can have all the checking
145    // in one place.
146    setVgap (vgap);
147    setHgap (hgap);
148    setAlignment (align);
149  }
150
151  /** Lay out the container's components based on current settings.
152   * @param parent The parent container
153   */
154  public void layoutContainer (Container parent)
155  {
156    synchronized (parent.getTreeLock ())
157      {
158        int num = parent.getComponentCount ();
159        // This is more efficient than calling getComponents().
160        Component[] comps = parent.component;
161
162        Dimension d = parent.getSize ();
163        Insets ins = parent.getInsets ();
164
165        ComponentOrientation orient = parent.getComponentOrientation ();
166        boolean left_to_right = orient.isLeftToRight ();
167
168        int y = ins.top + vgap;
169        int i = 0;
170        while (i < num)
171          {
172            // Find the components which go in the current row.
173            int new_w = ins.left + hgap + ins.right;
174            int new_h = 0;
175            int j;
176            boolean found_one = false;
177            for (j = i; j < num; ++j)
178              {
179                // Skip invisible items.
180                if (! comps[j].visible)
181                  continue;
182
183                Dimension c = comps[j].getPreferredSize ();
184
185                int next_w = new_w + hgap + c.width;
186                if (next_w <= d.width || ! found_one)
187                  {
188                    new_w = next_w;
189                    new_h = Math.max (new_h, c.height);
190                    found_one = true;
191                  }
192                else
193                  {
194                    // Must start a new row, and we already found an item
195                    break;
196                  }
197              }
198
199            // Set the location of each component for this row.
200            int x;
201
202            int myalign = align;
203            if (align == LEADING)
204              myalign = left_to_right ? LEFT : RIGHT;
205            else if (align == TRAILING)
206              myalign = left_to_right ? RIGHT : LEFT;
207
208            if (myalign == RIGHT)
209              x = ins.left + (d.width - new_w) + hgap;
210            else if (myalign == CENTER)
211              x = ins.left + (d.width - new_w) / 2 + hgap;
212            else // LEFT and all other values of align.
213              x = ins.left + hgap;
214
215            for (int k = i; k < j; ++k)
216              {
217                if (comps[k].visible)
218                  {
219                    Dimension c = comps[k].getPreferredSize ();
220                    comps[k].setBounds (x, y + (new_h - c.height) / 2,
221                                        c.width, c.height);
222                    x += c.width + hgap;
223                  }
224              }
225
226            // Advance to next row.
227            i = j;
228            y += new_h + vgap;
229          }
230      }
231  }
232
233  /**
234   * Returns the minimum layout size for the specified container using
235   * this layout.
236   * @param cont The parent container
237   * @return The minimum layout size.
238   */
239  public Dimension minimumLayoutSize (Container cont)
240  {
241    return getSize (cont, true);
242  }
243
244  /**
245   * Returns the preferred layout size for the specified container using
246   * this layout.
247   * @param cont The parent container
248   * @return The preferred layout size.
249   */
250  public Dimension preferredLayoutSize (Container cont)
251  {
252    return getSize (cont, false);
253  }
254
255  /** Remove the indicated component from this layout manager.
256   * This particular implementation does nothing.
257   * @param comp The component to remove
258   */
259  public void removeLayoutComponent (Component comp)
260  {
261    // Nothing.
262  }
263
264  /**
265   * Sets the justification value for this object to the specified value.
266   *
267   * @param align The new justification value for this object, which must
268   * be one of the constants in this class.
269   */
270  public void setAlignment (int align)
271  {
272    // The JDK accepts invalid values and treats them as
273    // LEFT during layout, so do we. The invalid value is even stored,
274    // getAlignment() returns the same invalid value.
275    this.align = align;
276  }
277
278  /**
279   * Sets the horizontal gap between lines of components to the specified value.
280   * No Exception is thrown if hgap < 0.
281   * 
282   * @param hgap The new horizontal gap between components.
283   */
284  public void setHgap (int hgap)
285  {
286    this.hgap = hgap;
287  }
288
289  /**
290   * Sets the vertical gap between lines of components to the specified value.
291   * No Exception is thrown if vgap < 0.
292   *
293   * @param vgap The new vertical gap.
294   */
295  public void setVgap (int vgap)
296  {
297    this.vgap = vgap;
298  }
299
300  /** Return String description of this object.
301   * @return A string representation of this object.
302   */
303  public String toString ()
304  {
305    return ("[" + getClass ().getName () + ",hgap=" + hgap + ",vgap=" + vgap
306            + ",align=" + align + "]");
307  }
308
309  // This method is used to compute the various sizes.
310  private Dimension getSize (Container parent, boolean is_min)
311  {
312    synchronized (parent.getTreeLock ())
313      {
314        int w, h, num = parent.getComponentCount ();
315        // This is more efficient than calling getComponents().
316        Component[] comps = parent.component;
317
318        w = 0;
319        h = 0;
320        for (int i = 0; i < num; ++i)
321          {
322            if (! comps[i].visible)
323              continue;
324
325            // FIXME: can we just directly read the fields in Component?
326            // Or will that not work with subclassing?
327            Dimension d;
328
329            if (is_min)
330              d = comps[i].getMinimumSize ();
331            else
332              d = comps[i].getPreferredSize ();
333
334            w += d.width;
335            h = Math.max (d.height, h);
336          }
337
338        Insets ins = parent.getInsets ();
339
340        if (num == 0)
341          w += 2 * hgap + ins.left + ins.right;
342        else
343          w += (num + 1) * hgap + ins.left + ins.right;
344        h += 2 * vgap + ins.top + ins.bottom;
345
346        return new Dimension (w, h);
347      }
348  }
349
350  /**
351   * @serial The justification alignment of the lines of components, which
352   * will be one of the constants defined in this class.
353   */
354  private int align;
355
356  /**
357   * @serial The horizontal gap between components.
358   */
359  private int hgap;
360
361  /**
362   * @serial The vertical gap between lines of components.
363   */
364  private int vgap;
365}