1 /* GridLayout.java -- Grid-based layout engine
2    Copyright (C) 1999, 2000, 2002, 2004  Free Software Foundation
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.awt;
40 
41 import java.io.Serializable;
42 
43 /** This class implements a grid-based layout scheme.  Components are
44  * all given the same size and are laid out from left to right and top
45  * to bottom.  A GridLayout is configured with a number of rows and a
46  * number of columns.  If both are specified, then the number of
47  * columns is ignored and is derived from the number of rows and the
48  * total number of components.  If either is zero then that dimension
49  * is computed based on the actual size of the container.  An
50  * exception is thrown if an attempt is made to set both the number of
51  * rows and the number of columns to 0.  This class also supports
52  * horizontal and vertical gaps; these are used as spacing between
53  * cells.
54  *
55  * @author Tom Tromey (tromey@redhat.com)
56  * @author Aaron M. Renn (arenn@urbanophile.com)
57  */
58 public class GridLayout implements LayoutManager, Serializable
59 {
60   static final long serialVersionUID = -7411804673224730901L;
61 
62   /** Add a new component to the layout.  This particular implementation
63    * does nothing.
64    * @param name The name of the component to add.
65    * @param comp The component to add.
66    */
addLayoutComponent(String name, Component comp)67   public void addLayoutComponent (String name, Component comp)
68   {
69     // Nothing.
70   }
71 
72   /** Return the number of columns in this layout.  */
getColumns()73   public int getColumns ()
74   {
75     return cols;
76   }
77 
78   /** Return the horizontal gap.  */
getHgap()79   public int getHgap ()
80   {
81     return hgap;
82   }
83 
84   /** Return the number of rows in this layout.  */
getRows()85   public int getRows ()
86   {
87     return rows;
88   }
89 
90   /** Return the vertical gap.  */
getVgap()91   public int getVgap ()
92   {
93     return vgap;
94   }
95 
96   /** Create a new <code>GridLayout</code> with one row and any number
97    * of columns.  Both gaps are set to 0.
98    */
GridLayout()99   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    */
GridLayout(int rows, int cols)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    */
GridLayout(int rows, int cols, int hgap, int vgap)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    */
layoutContainer(Container parent)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    */
minimumLayoutSize(Container cont)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    */
preferredLayoutSize(Container cont)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    */
removeLayoutComponent(Component comp)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    */
setColumns(int newCols)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    */
setHgap(int hgap)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    */
setRows(int newRows)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    */
setVgap(int vgap)283   public void setVgap (int vgap)
284   {
285     this.vgap = vgap;
286   }
287 
288   /** Return String description of this object.  */
toString()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.
getSize(Container parent, boolean is_min)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 }
355