001    /* FlowLayout.java -- Grid-based layout engine
002       Copyright (C) 1999, 2000, 2001, 2002, 2004  Free Software Foundation
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.awt;
040    
041    import 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     */
052    public 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    }