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