1 /* 2 * Copyright (c) 1995, 2017, 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} 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} 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} 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"> 59 * <caption style="display:none">Figures</caption> 60 * <tr style="text-align:center"> 61 * <td><img SRC="doc-files/GridLayout-1.gif" 62 * alt="Shows 6 buttons in rows of 2. Row 1 shows buttons 1 then 2. 63 * Row 2 shows buttons 3 then 4. Row 3 shows buttons 5 then 6."> 64 * </td> 65 * 66 * <td style="text-align:center"><img SRC="doc-files/GridLayout-2.gif" 67 * alt="Shows 6 buttons in rows of 2. Row 1 shows buttons 2 then 1. 68 * Row 2 shows buttons 4 then 3. Row 3 shows buttons 6 then 5."> 69 * </td> 70 * </tr> 71 * 72 * <tr style="text-align:center"> 73 * <td>Figure 1: Horizontal, Left-to-Right</td> 74 * 75 * <td>Figure 2: Horizontal, Right-to-Left</td> 76 * </tr> 77 * </table> 78 * <p> 79 * When both the number of rows and the number of columns have 80 * been set to non-zero values, either by a constructor or 81 * by the {@code setRows} and {@code setColumns} methods, the number of 82 * columns specified is ignored. Instead, the number of 83 * columns is determined from the specified number of rows 84 * and the total number of components in the layout. So, for 85 * example, if three rows and two columns have been specified 86 * and nine components are added to the layout, they will 87 * be displayed as three rows of three columns. Specifying 88 * the number of columns affects the layout only when the 89 * number of rows is set to zero. 90 * 91 * @author Arthur van Hoff 92 * @since 1.0 93 */ 94 public class GridLayout implements LayoutManager, java.io.Serializable { 95 /* 96 * serialVersionUID 97 */ 98 private static final long serialVersionUID = -7411804673224730901L; 99 100 /** 101 * This is the horizontal gap (in pixels) which specifies the space 102 * between columns. They can be changed at any time. 103 * This should be a non-negative integer. 104 * 105 * @serial 106 * @see #getHgap() 107 * @see #setHgap(int) 108 */ 109 int hgap; 110 /** 111 * This is the vertical gap (in pixels) which specifies the space 112 * between rows. They can be changed at any time. 113 * This should be a non negative integer. 114 * 115 * @serial 116 * @see #getVgap() 117 * @see #setVgap(int) 118 */ 119 int vgap; 120 /** 121 * This is the number of rows specified for the grid. The number 122 * of rows can be changed at any time. 123 * This should be a non negative integer, where '0' means 124 * 'any number' meaning that the number of Rows in that 125 * dimension depends on the other dimension. 126 * 127 * @serial 128 * @see #getRows() 129 * @see #setRows(int) 130 */ 131 int rows; 132 /** 133 * This is the number of columns specified for the grid. The number 134 * of columns can be changed at any time. 135 * This should be a non negative integer, where '0' means 136 * 'any number' meaning that the number of Columns in that 137 * dimension depends on the other dimension. 138 * 139 * @serial 140 * @see #getColumns() 141 * @see #setColumns(int) 142 */ 143 int cols; 144 145 /** 146 * Creates a grid layout with a default of one column per component, 147 * in a single row. 148 * @since 1.1 149 */ 150 public GridLayout() { 151 this(1, 0, 0, 0); 152 } 153 154 /** 155 * Creates a grid layout with the specified number of rows and 156 * columns. All components in the layout are given equal size. 157 * <p> 158 * One, but not both, of {@code rows} and {@code cols} can 159 * be zero, which means that any number of objects can be placed in a 160 * row or in a column. 161 * @param rows the rows, with the value zero meaning 162 * any number of rows. 163 * @param cols the columns, with the value zero meaning 164 * any number of columns. 165 */ 166 public GridLayout(int rows, int cols) { 167 this(rows, cols, 0, 0); 168 } 169 170 /** 171 * Creates a grid layout with the specified number of rows and 172 * columns. All components in the layout are given equal size. 173 * <p> 174 * In addition, the horizontal and vertical gaps are set to the 175 * specified values. Horizontal gaps are placed between each 176 * of the columns. Vertical gaps are placed between each of 177 * the rows. 178 * <p> 179 * One, but not both, of {@code rows} and {@code cols} can 180 * be zero, which means that any number of objects can be placed in a 181 * row or in a column. 182 * <p> 183 * All {@code GridLayout} constructors defer to this one. 184 * @param rows the rows, with the value zero meaning 185 * any number of rows 186 * @param cols the columns, with the value zero meaning 187 * any number of columns 188 * @param hgap the horizontal gap 189 * @param vgap the vertical gap 190 * @exception IllegalArgumentException if the value of both 191 * {@code rows} and {@code cols} is 192 * set to zero 193 */ 194 public GridLayout(int rows, int cols, int hgap, int vgap) { 195 if ((rows == 0) && (cols == 0)) { 196 throw new IllegalArgumentException("rows and cols cannot both be zero"); 197 } 198 this.rows = rows; 199 this.cols = cols; 200 this.hgap = hgap; 201 this.vgap = vgap; 202 } 203 204 /** 205 * Gets the number of rows in this layout. 206 * @return the number of rows in this layout 207 * @since 1.1 208 */ 209 public int getRows() { 210 return rows; 211 } 212 213 /** 214 * Sets the number of rows in this layout to the specified value. 215 * @param rows the number of rows in this layout 216 * @exception IllegalArgumentException if the value of both 217 * {@code rows} and {@code cols} is set to zero 218 * @since 1.1 219 */ 220 public void setRows(int rows) { 221 if ((rows == 0) && (this.cols == 0)) { 222 throw new IllegalArgumentException("rows and cols cannot both be zero"); 223 } 224 this.rows = rows; 225 } 226 227 /** 228 * Gets the number of columns in this layout. 229 * @return the number of columns in this layout 230 * @since 1.1 231 */ 232 public int getColumns() { 233 return cols; 234 } 235 236 /** 237 * Sets the number of columns in this layout to the specified value. 238 * Setting the number of columns has no affect on the layout 239 * if the number of rows specified by a constructor or by 240 * the {@code setRows} method is non-zero. In that case, the number 241 * of columns displayed in the layout is determined by the total 242 * number of components and the number of rows specified. 243 * @param cols the number of columns in this layout 244 * @exception IllegalArgumentException if the value of both 245 * {@code rows} and {@code cols} is set to zero 246 * @since 1.1 247 */ 248 public void setColumns(int cols) { 249 if ((cols == 0) && (this.rows == 0)) { 250 throw new IllegalArgumentException("rows and cols cannot both be zero"); 251 } 252 this.cols = cols; 253 } 254 255 /** 256 * Gets the horizontal gap between components. 257 * @return the horizontal gap between components 258 * @since 1.1 259 */ 260 public int getHgap() { 261 return hgap; 262 } 263 264 /** 265 * Sets the horizontal gap between components to the specified value. 266 * @param hgap the horizontal gap between components 267 * @since 1.1 268 */ 269 public void setHgap(int hgap) { 270 this.hgap = hgap; 271 } 272 273 /** 274 * Gets the vertical gap between components. 275 * @return the vertical gap between components 276 * @since 1.1 277 */ 278 public int getVgap() { 279 return vgap; 280 } 281 282 /** 283 * Sets the vertical gap between components to the specified value. 284 * @param vgap the vertical gap between components 285 * @since 1.1 286 */ 287 public void setVgap(int vgap) { 288 this.vgap = vgap; 289 } 290 291 /** 292 * Adds the specified component with the specified name to the layout. 293 * @param name the name of the component 294 * @param comp the component to be added 295 */ 296 public void addLayoutComponent(String name, Component comp) { 297 } 298 299 /** 300 * Removes the specified component from the layout. 301 * @param comp the component to be removed 302 */ 303 public void removeLayoutComponent(Component comp) { 304 } 305 306 /** 307 * Determines the preferred size of the container argument using 308 * this grid layout. 309 * <p> 310 * The preferred width of a grid layout is the largest preferred 311 * width of all of the components in the container times the number of 312 * columns, plus the horizontal padding times the number of columns 313 * minus one, plus the left and right insets of the target container. 314 * <p> 315 * The preferred height of a grid layout is the largest preferred 316 * height of all of the components in the container times the number of 317 * rows, plus the vertical padding times the number of rows minus one, 318 * plus the top and bottom insets of the target container. 319 * 320 * @param parent the container in which to do the layout 321 * @return the preferred dimensions to lay out the 322 * subcomponents of the specified container 323 * @see java.awt.GridLayout#minimumLayoutSize 324 * @see java.awt.Container#getPreferredSize() 325 */ 326 public Dimension preferredLayoutSize(Container parent) { 327 synchronized (parent.getTreeLock()) { 328 Insets insets = parent.getInsets(); 329 int ncomponents = parent.getComponentCount(); 330 int nrows = rows; 331 int ncols = cols; 332 333 if (nrows > 0) { 334 ncols = (ncomponents + nrows - 1) / nrows; 335 } else { 336 nrows = (ncomponents + ncols - 1) / ncols; 337 } 338 int w = 0; 339 int h = 0; 340 for (int i = 0 ; i < ncomponents ; i++) { 341 Component comp = parent.getComponent(i); 342 Dimension d = comp.getPreferredSize(); 343 if (w < d.width) { 344 w = d.width; 345 } 346 if (h < d.height) { 347 h = d.height; 348 } 349 } 350 return new Dimension(insets.left + insets.right + ncols*w + (ncols-1)*hgap, 351 insets.top + insets.bottom + nrows*h + (nrows-1)*vgap); 352 } 353 } 354 355 /** 356 * Determines the minimum size of the container argument using this 357 * grid layout. 358 * <p> 359 * The minimum width of a grid layout is the largest minimum width 360 * of all of the components in the container times the number of columns, 361 * plus the horizontal padding times the number of columns minus one, 362 * plus the left and right insets of the target container. 363 * <p> 364 * The minimum height of a grid layout is the largest minimum height 365 * of all of the components in the container times the number of rows, 366 * plus the vertical padding times the number of rows minus one, plus 367 * the top and bottom insets of the target container. 368 * 369 * @param parent the container in which to do the layout 370 * @return the minimum dimensions needed to lay out the 371 * subcomponents of the specified container 372 * @see java.awt.GridLayout#preferredLayoutSize 373 * @see java.awt.Container#doLayout 374 */ 375 public Dimension minimumLayoutSize(Container parent) { 376 synchronized (parent.getTreeLock()) { 377 Insets insets = parent.getInsets(); 378 int ncomponents = parent.getComponentCount(); 379 int nrows = rows; 380 int ncols = cols; 381 382 if (nrows > 0) { 383 ncols = (ncomponents + nrows - 1) / nrows; 384 } else { 385 nrows = (ncomponents + ncols - 1) / ncols; 386 } 387 int w = 0; 388 int h = 0; 389 for (int i = 0 ; i < ncomponents ; i++) { 390 Component comp = parent.getComponent(i); 391 Dimension d = comp.getMinimumSize(); 392 if (w < d.width) { 393 w = d.width; 394 } 395 if (h < d.height) { 396 h = d.height; 397 } 398 } 399 return new Dimension(insets.left + insets.right + ncols*w + (ncols-1)*hgap, 400 insets.top + insets.bottom + nrows*h + (nrows-1)*vgap); 401 } 402 } 403 404 /** 405 * Lays out the specified container using this layout. 406 * <p> 407 * This method reshapes the components in the specified target 408 * container in order to satisfy the constraints of the 409 * {@code GridLayout} object. 410 * <p> 411 * The grid layout manager determines the size of individual 412 * components by dividing the free space in the container into 413 * equal-sized portions according to the number of rows and columns 414 * in the layout. The container's free space equals the container's 415 * size minus any insets and any specified horizontal or vertical 416 * gap. All components in a grid layout are given the same size. 417 * 418 * @param parent the container in which to do the layout 419 * @see java.awt.Container 420 * @see java.awt.Container#doLayout 421 */ 422 public void layoutContainer(Container parent) { 423 synchronized (parent.getTreeLock()) { 424 Insets insets = parent.getInsets(); 425 int ncomponents = parent.getComponentCount(); 426 int nrows = rows; 427 int ncols = cols; 428 boolean ltr = parent.getComponentOrientation().isLeftToRight(); 429 430 if (ncomponents == 0) { 431 return; 432 } 433 if (nrows > 0) { 434 ncols = (ncomponents + nrows - 1) / nrows; 435 } else { 436 nrows = (ncomponents + ncols - 1) / ncols; 437 } 438 // 4370316. To position components in the center we should: 439 // 1. get an amount of extra space within Container 440 // 2. incorporate half of that value to the left/top position 441 // Note that we use trancating division for widthOnComponent 442 // The reminder goes to extraWidthAvailable 443 int totalGapsWidth = (ncols - 1) * hgap; 444 int widthWOInsets = parent.width - (insets.left + insets.right); 445 int widthOnComponent = (widthWOInsets - totalGapsWidth) / ncols; 446 int extraWidthAvailable = (widthWOInsets - (widthOnComponent * ncols + totalGapsWidth)) / 2; 447 448 int totalGapsHeight = (nrows - 1) * vgap; 449 int heightWOInsets = parent.height - (insets.top + insets.bottom); 450 int heightOnComponent = (heightWOInsets - totalGapsHeight) / nrows; 451 int extraHeightAvailable = (heightWOInsets - (heightOnComponent * nrows + totalGapsHeight)) / 2; 452 if (ltr) { 453 for (int c = 0, x = insets.left + extraWidthAvailable; c < ncols ; c++, x += widthOnComponent + hgap) { 454 for (int r = 0, y = insets.top + extraHeightAvailable; r < nrows ; r++, y += heightOnComponent + vgap) { 455 int i = r * ncols + c; 456 if (i < ncomponents) { 457 parent.getComponent(i).setBounds(x, y, widthOnComponent, heightOnComponent); 458 } 459 } 460 } 461 } else { 462 for (int c = 0, x = (parent.width - insets.right - widthOnComponent) - extraWidthAvailable; c < ncols ; c++, x -= widthOnComponent + hgap) { 463 for (int r = 0, y = insets.top + extraHeightAvailable; r < nrows ; r++, y += heightOnComponent + vgap) { 464 int i = r * ncols + c; 465 if (i < ncomponents) { 466 parent.getComponent(i).setBounds(x, y, widthOnComponent, heightOnComponent); 467 } 468 } 469 } 470 } 471 } 472 } 473 474 /** 475 * Returns the string representation of this grid layout's values. 476 * @return a string representation of this grid layout 477 */ 478 public String toString() { 479 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + 480 ",rows=" + rows + ",cols=" + cols + "]"; 481 } 482 }