001/* GridLayout.java -- Grid-based layout engine
002   Copyright (C) 1999, 2000, 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 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 */
058public 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}