001    /* GridLayout.java -- Grid-based layout engine
002       Copyright (C) 1999, 2000, 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 grid-based layout scheme.  Components are
044     * all given the same size and are laid out from left to right and top
045     * to bottom.  A GridLayout is configured with a number of rows and a
046     * number of columns.  If both are specified, then the number of
047     * columns is ignored and is derived from the number of rows and the
048     * total number of components.  If either is zero then that dimension
049     * is computed based on the actual size of the container.  An
050     * exception is thrown if an attempt is made to set both the number of
051     * rows and the number of columns to 0.  This class also supports
052     * horizontal and vertical gaps; these are used as spacing between
053     * cells.
054     *
055     * @author Tom Tromey (tromey@redhat.com)
056     * @author Aaron M. Renn (arenn@urbanophile.com)
057     */
058    public class GridLayout implements LayoutManager, Serializable
059    {
060      static final long serialVersionUID = -7411804673224730901L;
061      
062      /** Add a new component to the layout.  This particular implementation
063       * does nothing.
064       * @param name The name of the component to add.
065       * @param comp The component to add.
066       */
067      public void addLayoutComponent (String name, Component comp)
068      {
069        // Nothing.
070      }
071    
072      /** Return the number of columns in this layout.  */
073      public int getColumns ()
074      {
075        return cols;
076      }
077    
078      /** Return the horizontal gap.  */
079      public int getHgap ()
080      {
081        return hgap;
082      }
083    
084      /** Return the number of rows in this layout.  */
085      public int getRows ()
086      {
087        return rows;
088      }
089    
090      /** Return the vertical gap.  */
091      public int getVgap ()
092      {
093        return vgap;
094      }
095    
096      /** Create a new <code>GridLayout</code> with one row and any number
097       * of columns.  Both gaps are set to 0.
098       */
099      public GridLayout ()
100      {
101        this (1, 0, 0, 0);
102      }
103    
104      /** Create a new <code>GridLayout</code> with the specified number
105       * of rows and columns.  Both gaps are set to 0.  Note that the row
106       * and column settings cannot both be zero.  If both the row and
107       * column values are non-zero, the rows value takes precedence.
108       * @param rows Number of rows
109       * @param cols Number of columns
110       * @exception IllegalArgumentException If rows and columns are both
111       *        0, or if either are negative
112       */
113      public GridLayout (int rows, int cols)
114      {
115        this (rows, cols, 0, 0);
116      }
117    
118      /** Create a new GridLayout with the specified number of rows and
119       * columns and the specified gaps.
120       * Note that the row and column settings cannot both be
121       * zero.  If both the row and column values are non-zero, the rows value
122       * takes precedence.
123       * @param rows Number of rows
124       * @param cols Number of columns
125       * @param hgap The horizontal gap
126       * @param vgap The vertical gap
127       * @exception IllegalArgumentException If rows and columns are both
128       *        0, if either are negative, or if either gap is negative
129       */
130      public GridLayout (int rows, int cols, int hgap, int vgap)
131      {
132        if (rows < 0)
133          throw new IllegalArgumentException ("number of rows cannot be negative");
134        if (cols < 0)
135          throw new IllegalArgumentException ("number of columns cannot be negative");
136        if (rows == 0 && cols == 0)
137          throw new IllegalArgumentException ("both rows and columns cannot be 0");
138        if (hgap < 0)
139          throw new IllegalArgumentException ("horizontal gap must be nonnegative");
140        if (vgap < 0)
141          throw new IllegalArgumentException ("vertical gap must be nonnegative");
142        this.rows = rows;
143        this.cols = cols;
144        this.hgap = hgap;
145        this.vgap = vgap;
146      }
147    
148      /** Lay out the container's components based on current settings.
149       * The free space in the container is divided evenly into the specified
150       * number of rows and columns in this object.
151       * @param parent The container to lay out
152       */
153      public void layoutContainer (Container parent)
154      {
155        synchronized (parent.getTreeLock ())
156          {
157            int num = parent.ncomponents;
158    
159            // There's no point, and handling this would mean adding special
160            // cases.
161            if (num == 0)
162              return;
163    
164            // This is more efficient than calling getComponents().
165            Component[] comps = parent.component;
166    
167            int real_rows = rows;
168            int real_cols = cols;
169            if (real_rows == 0)
170              real_rows = (num + real_cols - 1) / real_cols;
171            else
172              real_cols = (num + real_rows - 1) / real_rows;
173    
174            // We might have less than a single row.  In this case we expand
175            // to fill.
176            if (num < real_cols)
177              real_cols = num;
178    
179            Dimension d = parent.getSize ();
180            Insets ins = parent.getInsets ();
181    
182            // Compute width and height of each cell in the grid.
183            int tw = d.width - ins.left - ins.right;
184            tw = (tw - (real_cols - 1) * hgap) / real_cols;
185            int th = d.height - ins.top - ins.bottom;
186            th = (th - (real_rows - 1) * vgap) / real_rows;
187    
188            // If the cells are too small, still try to do something.
189            if (tw < 0)
190              tw = 1;
191            if (th < 0)
192              th = 1;
193    
194            int x = ins.left;
195            int y = ins.top;
196            int i = 0;
197            int recount = 0;
198    
199            while (i < num)
200              {
201                comps[i].setBounds (x, y, tw, th);
202    
203                ++i;
204                ++recount;
205                if (recount == real_cols)
206                  {
207                    recount = 0;
208                    y += vgap + th;
209                    x = ins.left;
210                  }
211                else
212                  x += hgap + tw;
213              }
214          }
215      }
216    
217      /** Get the minimum layout size of the container.
218       * @param cont The parent container
219       */
220      public Dimension minimumLayoutSize (Container cont)
221      {
222        return getSize (cont, true);
223      }
224    
225      /** Get the preferred layout size of the container.
226       * @param cont The parent container
227       */
228      public Dimension preferredLayoutSize (Container cont)
229      {
230        return getSize (cont, false);
231      }
232    
233      /** Remove the indicated component from this layout manager.
234       * This particular implementation does nothing.
235       * @param comp The component to remove
236       */
237      public void removeLayoutComponent (Component comp)
238      {
239        // Nothing.
240      }
241    
242      /** Set the number of columns.
243       * @param newCols
244       * @exception IllegalArgumentException If the number of columns is
245       *     negative, or if the number of columns is zero and the number
246       *     of rows is already 0.
247       */
248      public void setColumns (int newCols)
249      {
250        if (newCols < 0)
251          throw new IllegalArgumentException ("number of columns cannot be negative");
252        if (newCols == 0 && rows == 0)
253          throw new IllegalArgumentException ("number of rows is already 0");
254        this.cols = newCols;
255      }
256    
257      /** Set the horizontal gap.  An Exception is not thrown if hgap < 0.
258       * @param hgap The horizontal gap
259       */
260      public void setHgap (int hgap)
261      {
262        this.hgap = hgap;
263      }
264    
265      /** Set the number of rows
266       * @param newRows
267       * @exception IllegalArgumentException If the number of rows is
268       *     negative, or if the number of rows is zero and the number
269       *     of columns is already 0.
270       */
271      public void setRows (int newRows)
272      {
273        if (newRows < 0)
274          throw new IllegalArgumentException ("number of rows cannot be negative");
275        if (newRows == 0 && cols == 0)
276          throw new IllegalArgumentException ("number of columns is already 0");
277        this.rows = newRows;
278      }
279    
280      /** Set the vertical gap.  An Exception is not thrown if vgap < 0.
281       * @param vgap The vertical gap
282       */
283      public void setVgap (int vgap)
284      {
285        this.vgap = vgap;
286      }
287    
288      /** Return String description of this object.  */
289      public String toString ()
290      {
291        return (getClass ().getName () + "["
292                + "hgap=" + hgap + ",vgap=" + vgap
293                + ",rows=" + rows + ",cols=" + cols
294                + "]");
295      }
296    
297      // This method is used to compute the various sizes.
298      private Dimension getSize (Container parent, boolean is_min)
299      {
300        synchronized (parent.getTreeLock ())
301          {
302            int w = 0, h = 0, num = parent.ncomponents;
303            // This is more efficient than calling getComponents().
304            Component[] comps = parent.component;
305    
306            for (int i = 0; i < num; ++i)
307              {
308                Dimension d;
309    
310                if (is_min)
311                  d = comps[i].getMinimumSize ();
312                else
313                  d = comps[i].getPreferredSize ();
314    
315                w = Math.max (d.width, w);
316                h = Math.max (d.height, h);
317              }
318    
319            int real_rows = rows;
320            int real_cols = cols;
321            if (real_rows == 0)
322              real_rows = (num + real_cols - 1) / real_cols;
323            else
324              real_cols = (num + real_rows - 1) / real_rows;
325    
326            Insets ins = parent.getInsets ();
327            // We subtract out an extra gap here because the gaps are only
328            // between cells.
329            w = ins.left + ins.right + real_cols * (w + hgap) - hgap;
330            h = ins.top + ins.bottom + real_rows * (h + vgap) - vgap;
331            return new Dimension (w, h);
332          }
333      }
334    
335      /**
336       * @serial The number of columns in the grid.
337       */
338      private int cols;
339    
340      /**
341       * @serial The number of rows in the grid.
342       */
343      private int rows;
344    
345      /**
346       * @serial The horizontal gap between columns
347       */
348      private int hgap;
349    
350      /**
351       * @serial The vertical gap between rows
352       */
353      private int vgap;
354    }