View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.awt;
27  
28  /**
29   * The <code>GridLayout</code> class is a layout manager that
30   * lays out a container's components in a rectangular grid.
31   * The container is divided into equal-sized rectangles,
32   * and one component is placed in each rectangle.
33   * For example, the following is an applet that lays out six buttons
34   * into three rows and two columns:
35   *
36   * <hr><blockquote>
37   * <pre>
38   * import java.awt.*;
39   * import java.applet.Applet;
40   * public class ButtonGrid extends Applet {
41   *     public void init() {
42   *         setLayout(new GridLayout(3,2));
43   *         add(new Button("1"));
44   *         add(new Button("2"));
45   *         add(new Button("3"));
46   *         add(new Button("4"));
47   *         add(new Button("5"));
48   *         add(new Button("6"));
49   *     }
50   * }
51   * </pre></blockquote><hr>
52   * <p>
53   * If the container's <code>ComponentOrientation</code> property is horizontal
54   * and left-to-right, the above example produces the output shown in Figure 1.
55   * If the container's <code>ComponentOrientation</code> property is horizontal
56   * and right-to-left, the example produces the output shown in Figure 2.
57   *
58   * <table style="float:center" WIDTH=600 summary="layout">
59   * <tr ALIGN=CENTER>
60   * <td><img SRC="doc-files/GridLayout-1.gif"
61   *      alt="Shows 6 buttons in rows of 2. Row 1 shows buttons 1 then 2.
62   * Row 2 shows buttons 3 then 4. Row 3 shows buttons 5 then 6.">
63   * </td>
64   *
65   * <td ALIGN=CENTER><img SRC="doc-files/GridLayout-2.gif"
66   *                   alt="Shows 6 buttons in rows of 2. Row 1 shows buttons 2 then 1.
67   * Row 2 shows buttons 4 then 3. Row 3 shows buttons 6 then 5.">
68   * </td>
69   * </tr>
70   *
71   * <tr ALIGN=CENTER>
72   * <td>Figure 1: Horizontal, Left-to-Right</td>
73   *
74   * <td>Figure 2: Horizontal, Right-to-Left</td>
75   * </tr>
76   * </table>
77   * <p>
78   * When both the number of rows and the number of columns have
79   * been set to non-zero values, either by a constructor or
80   * by the <tt>setRows</tt> and <tt>setColumns</tt> methods, the number of
81   * columns specified is ignored.  Instead, the number of
82   * columns is determined from the specified number of rows
83   * and the total number of components in the layout. So, for
84   * example, if three rows and two columns have been specified
85   * and nine components are added to the layout, they will
86   * be displayed as three rows of three columns.  Specifying
87   * the number of columns affects the layout only when the
88   * number of rows is set to zero.
89   *
90   * @author  Arthur van Hoff
91   * @since   JDK1.0
92   */
93  public class GridLayout implements LayoutManager, java.io.Serializable {
94      /*
95       * serialVersionUID
96       */
97      private static final long serialVersionUID = -7411804673224730901L;
98  
99      /**
100      * This is the horizontal gap (in pixels) which specifies the space
101      * between columns.  They can be changed at any time.
102      * This should be a non-negative integer.
103      *
104      * @serial
105      * @see #getHgap()
106      * @see #setHgap(int)
107      */
108     int hgap;
109     /**
110      * This is the vertical gap (in pixels) which specifies the space
111      * between rows.  They can be changed at any time.
112      * This should be a non negative integer.
113      *
114      * @serial
115      * @see #getVgap()
116      * @see #setVgap(int)
117      */
118     int vgap;
119     /**
120      * This is the number of rows specified for the grid.  The number
121      * of rows can be changed at any time.
122      * This should be a non negative integer, where '0' means
123      * 'any number' meaning that the number of Rows in that
124      * dimension depends on the other dimension.
125      *
126      * @serial
127      * @see #getRows()
128      * @see #setRows(int)
129      */
130     int rows;
131     /**
132      * This is the number of columns specified for the grid.  The number
133      * of columns can be changed at any time.
134      * This should be a non negative integer, where '0' means
135      * 'any number' meaning that the number of Columns in that
136      * dimension depends on the other dimension.
137      *
138      * @serial
139      * @see #getColumns()
140      * @see #setColumns(int)
141      */
142     int cols;
143 
144     /**
145      * Creates a grid layout with a default of one column per component,
146      * in a single row.
147      * @since JDK1.1
148      */
149     public GridLayout() {
150         this(1, 0, 0, 0);
151     }
152 
153     /**
154      * Creates a grid layout with the specified number of rows and
155      * columns. All components in the layout are given equal size.
156      * <p>
157      * One, but not both, of <code>rows</code> and <code>cols</code> can
158      * be zero, which means that any number of objects can be placed in a
159      * row or in a column.
160      * @param     rows   the rows, with the value zero meaning
161      *                   any number of rows.
162      * @param     cols   the columns, with the value zero meaning
163      *                   any number of columns.
164      */
165     public GridLayout(int rows, int cols) {
166         this(rows, cols, 0, 0);
167     }
168 
169     /**
170      * Creates a grid layout with the specified number of rows and
171      * columns. All components in the layout are given equal size.
172      * <p>
173      * In addition, the horizontal and vertical gaps are set to the
174      * specified values. Horizontal gaps are placed between each
175      * of the columns. Vertical gaps are placed between each of
176      * the rows.
177      * <p>
178      * One, but not both, of <code>rows</code> and <code>cols</code> can
179      * be zero, which means that any number of objects can be placed in a
180      * row or in a column.
181      * <p>
182      * All <code>GridLayout</code> constructors defer to this one.
183      * @param     rows   the rows, with the value zero meaning
184      *                   any number of rows
185      * @param     cols   the columns, with the value zero meaning
186      *                   any number of columns
187      * @param     hgap   the horizontal gap
188      * @param     vgap   the vertical gap
189      * @exception   IllegalArgumentException  if the value of both
190      *                  <code>rows</code> and <code>cols</code> is
191      *                  set to zero
192      */
193     public GridLayout(int rows, int cols, int hgap, int vgap) {
194         if ((rows == 0) && (cols == 0)) {
195             throw new IllegalArgumentException("rows and cols cannot both be zero");
196         }
197         this.rows = rows;
198         this.cols = cols;
199         this.hgap = hgap;
200         this.vgap = vgap;
201     }
202 
203     /**
204      * Gets the number of rows in this layout.
205      * @return    the number of rows in this layout
206      * @since     JDK1.1
207      */
208     public int getRows() {
209         return rows;
210     }
211 
212     /**
213      * Sets the number of rows in this layout to the specified value.
214      * @param        rows   the number of rows in this layout
215      * @exception    IllegalArgumentException  if the value of both
216      *               <code>rows</code> and <code>cols</code> is set to zero
217      * @since        JDK1.1
218      */
219     public void setRows(int rows) {
220         if ((rows == 0) && (this.cols == 0)) {
221             throw new IllegalArgumentException("rows and cols cannot both be zero");
222         }
223         this.rows = rows;
224     }
225 
226     /**
227      * Gets the number of columns in this layout.
228      * @return     the number of columns in this layout
229      * @since      JDK1.1
230      */
231     public int getColumns() {
232         return cols;
233     }
234 
235     /**
236      * Sets the number of columns in this layout to the specified value.
237      * Setting the number of columns has no affect on the layout
238      * if the number of rows specified by a constructor or by
239      * the <tt>setRows</tt> method is non-zero. In that case, the number
240      * of columns displayed in the layout is determined by the total
241      * number of components and the number of rows specified.
242      * @param        cols   the number of columns in this layout
243      * @exception    IllegalArgumentException  if the value of both
244      *               <code>rows</code> and <code>cols</code> is set to zero
245      * @since        JDK1.1
246      */
247     public void setColumns(int cols) {
248         if ((cols == 0) && (this.rows == 0)) {
249             throw new IllegalArgumentException("rows and cols cannot both be zero");
250         }
251         this.cols = cols;
252     }
253 
254     /**
255      * Gets the horizontal gap between components.
256      * @return       the horizontal gap between components
257      * @since        JDK1.1
258      */
259     public int getHgap() {
260         return hgap;
261     }
262 
263     /**
264      * Sets the horizontal gap between components to the specified value.
265      * @param        hgap   the horizontal gap between components
266      * @since        JDK1.1
267      */
268     public void setHgap(int hgap) {
269         this.hgap = hgap;
270     }
271 
272     /**
273      * Gets the vertical gap between components.
274      * @return       the vertical gap between components
275      * @since        JDK1.1
276      */
277     public int getVgap() {
278         return vgap;
279     }
280 
281     /**
282      * Sets the vertical gap between components to the specified value.
283      * @param         vgap  the vertical gap between components
284      * @since        JDK1.1
285      */
286     public void setVgap(int vgap) {
287         this.vgap = vgap;
288     }
289 
290     /**
291      * Adds the specified component with the specified name to the layout.
292      * @param name the name of the component
293      * @param comp the component to be added
294      */
295     public void addLayoutComponent(String name, Component comp) {
296     }
297 
298     /**
299      * Removes the specified component from the layout.
300      * @param comp the component to be removed
301      */
302     public void removeLayoutComponent(Component comp) {
303     }
304 
305     /**
306      * Determines the preferred size of the container argument using
307      * this grid layout.
308      * <p>
309      * The preferred width of a grid layout is the largest preferred
310      * width of all of the components in the container times the number of
311      * columns, plus the horizontal padding times the number of columns
312      * minus one, plus the left and right insets of the target container.
313      * <p>
314      * The preferred height of a grid layout is the largest preferred
315      * height of all of the components in the container times the number of
316      * rows, plus the vertical padding times the number of rows minus one,
317      * plus the top and bottom insets of the target container.
318      *
319      * @param     parent   the container in which to do the layout
320      * @return    the preferred dimensions to lay out the
321      *                      subcomponents of the specified container
322      * @see       java.awt.GridLayout#minimumLayoutSize
323      * @see       java.awt.Container#getPreferredSize()
324      */
325     public Dimension preferredLayoutSize(Container parent) {
326       synchronized (parent.getTreeLock()) {
327         Insets insets = parent.getInsets();
328         int ncomponents = parent.getComponentCount();
329         int nrows = rows;
330         int ncols = cols;
331 
332         if (nrows > 0) {
333             ncols = (ncomponents + nrows - 1) / nrows;
334         } else {
335             nrows = (ncomponents + ncols - 1) / ncols;
336         }
337         int w = 0;
338         int h = 0;
339         for (int i = 0 ; i < ncomponents ; i++) {
340             Component comp = parent.getComponent(i);
341             Dimension d = comp.getPreferredSize();
342             if (w < d.width) {
343                 w = d.width;
344             }
345             if (h < d.height) {
346                 h = d.height;
347             }
348         }
349         return new Dimension(insets.left + insets.right + ncols*w + (ncols-1)*hgap,
350                              insets.top + insets.bottom + nrows*h + (nrows-1)*vgap);
351       }
352     }
353 
354     /**
355      * Determines the minimum size of the container argument using this
356      * grid layout.
357      * <p>
358      * The minimum width of a grid layout is the largest minimum width
359      * of all of the components in the container times the number of columns,
360      * plus the horizontal padding times the number of columns minus one,
361      * plus the left and right insets of the target container.
362      * <p>
363      * The minimum height of a grid layout is the largest minimum height
364      * of all of the components in the container times the number of rows,
365      * plus the vertical padding times the number of rows minus one, plus
366      * the top and bottom insets of the target container.
367      *
368      * @param       parent   the container in which to do the layout
369      * @return      the minimum dimensions needed to lay out the
370      *                      subcomponents of the specified container
371      * @see         java.awt.GridLayout#preferredLayoutSize
372      * @see         java.awt.Container#doLayout
373      */
374     public Dimension minimumLayoutSize(Container parent) {
375       synchronized (parent.getTreeLock()) {
376         Insets insets = parent.getInsets();
377         int ncomponents = parent.getComponentCount();
378         int nrows = rows;
379         int ncols = cols;
380 
381         if (nrows > 0) {
382             ncols = (ncomponents + nrows - 1) / nrows;
383         } else {
384             nrows = (ncomponents + ncols - 1) / ncols;
385         }
386         int w = 0;
387         int h = 0;
388         for (int i = 0 ; i < ncomponents ; i++) {
389             Component comp = parent.getComponent(i);
390             Dimension d = comp.getMinimumSize();
391             if (w < d.width) {
392                 w = d.width;
393             }
394             if (h < d.height) {
395                 h = d.height;
396             }
397         }
398         return new Dimension(insets.left + insets.right + ncols*w + (ncols-1)*hgap,
399                              insets.top + insets.bottom + nrows*h + (nrows-1)*vgap);
400       }
401     }
402 
403     /**
404      * Lays out the specified container using this layout.
405      * <p>
406      * This method reshapes the components in the specified target
407      * container in order to satisfy the constraints of the
408      * <code>GridLayout</code> object.
409      * <p>
410      * The grid layout manager determines the size of individual
411      * components by dividing the free space in the container into
412      * equal-sized portions according to the number of rows and columns
413      * in the layout. The container's free space equals the container's
414      * size minus any insets and any specified horizontal or vertical
415      * gap. All components in a grid layout are given the same size.
416      *
417      * @param      parent   the container in which to do the layout
418      * @see        java.awt.Container
419      * @see        java.awt.Container#doLayout
420      */
421     public void layoutContainer(Container parent) {
422       synchronized (parent.getTreeLock()) {
423         Insets insets = parent.getInsets();
424         int ncomponents = parent.getComponentCount();
425         int nrows = rows;
426         int ncols = cols;
427         boolean ltr = parent.getComponentOrientation().isLeftToRight();
428 
429         if (ncomponents == 0) {
430             return;
431         }
432         if (nrows > 0) {
433             ncols = (ncomponents + nrows - 1) / nrows;
434         } else {
435             nrows = (ncomponents + ncols - 1) / ncols;
436         }
437         // 4370316. To position components in the center we should:
438         // 1. get an amount of extra space within Container
439         // 2. incorporate half of that value to the left/top position
440         // Note that we use trancating division for widthOnComponent
441         // The reminder goes to extraWidthAvailable
442         int totalGapsWidth = (ncols - 1) * hgap;
443         int widthWOInsets = parent.width - (insets.left + insets.right);
444         int widthOnComponent = (widthWOInsets - totalGapsWidth) / ncols;
445         int extraWidthAvailable = (widthWOInsets - (widthOnComponent * ncols + totalGapsWidth)) / 2;
446 
447         int totalGapsHeight = (nrows - 1) * vgap;
448         int heightWOInsets = parent.height - (insets.top + insets.bottom);
449         int heightOnComponent = (heightWOInsets - totalGapsHeight) / nrows;
450         int extraHeightAvailable = (heightWOInsets - (heightOnComponent * nrows + totalGapsHeight)) / 2;
451         if (ltr) {
452             for (int c = 0, x = insets.left + extraWidthAvailable; c < ncols ; c++, x += widthOnComponent + hgap) {
453                 for (int r = 0, y = insets.top + extraHeightAvailable; r < nrows ; r++, y += heightOnComponent + vgap) {
454                     int i = r * ncols + c;
455                     if (i < ncomponents) {
456                         parent.getComponent(i).setBounds(x, y, widthOnComponent, heightOnComponent);
457                     }
458                 }
459             }
460         } else {
461             for (int c = 0, x = (parent.width - insets.right - widthOnComponent) - extraWidthAvailable; c < ncols ; c++, x -= widthOnComponent + hgap) {
462                 for (int r = 0, y = insets.top + extraHeightAvailable; r < nrows ; r++, y += heightOnComponent + vgap) {
463                     int i = r * ncols + c;
464                     if (i < ncomponents) {
465                         parent.getComponent(i).setBounds(x, y, widthOnComponent, heightOnComponent);
466                     }
467                 }
468             }
469         }
470       }
471     }
472 
473     /**
474      * Returns the string representation of this grid layout's values.
475      * @return     a string representation of this grid layout
476      */
477     public String toString() {
478         return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap +
479                                        ",rows=" + rows + ",cols=" + cols + "]";
480     }
481 }