001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * -------------------- 028 * GridArrangement.java 029 * -------------------- 030 * (C) Copyright 2005-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Tracy Hiltbrand (define hashCode); 034 * 035 */ 036 037package org.jfree.chart.block; 038 039import java.awt.Graphics2D; 040import java.awt.geom.Rectangle2D; 041import java.io.Serializable; 042import java.util.Iterator; 043import java.util.List; 044import org.jfree.chart.ui.Size2D; 045 046/** 047 * Arranges blocks in a grid within their container. 048 */ 049public class GridArrangement implements Arrangement, Serializable { 050 051 /** For serialization. */ 052 private static final long serialVersionUID = -2563758090144655938L; 053 054 /** The rows. */ 055 private int rows; 056 057 /** The columns. */ 058 private int columns; 059 060 /** 061 * Creates a new grid arrangement. 062 * 063 * @param rows the row count. 064 * @param columns the column count. 065 */ 066 public GridArrangement(int rows, int columns) { 067 this.rows = rows; 068 this.columns = columns; 069 } 070 071 /** 072 * Adds a block and a key which can be used to determine the position of 073 * the block in the arrangement. This method is called by the container 074 * (you don't need to call this method directly) and gives the arrangement 075 * an opportunity to record the details if they are required. 076 * 077 * @param block the block. 078 * @param key the key ({@code null} permitted). 079 */ 080 @Override 081 public void add(Block block, Object key) { 082 // can safely ignore 083 } 084 085 /** 086 * Arranges the blocks within the specified container, subject to the given 087 * constraint. 088 * 089 * @param container the container ({@code null} not permitted). 090 * @param constraint the constraint. 091 * @param g2 the graphics device. 092 * 093 * @return The size following the arrangement. 094 */ 095 @Override 096 public Size2D arrange(BlockContainer container, Graphics2D g2, 097 RectangleConstraint constraint) { 098 LengthConstraintType w = constraint.getWidthConstraintType(); 099 LengthConstraintType h = constraint.getHeightConstraintType(); 100 if (w == LengthConstraintType.NONE) { 101 if (h == LengthConstraintType.NONE) { 102 return arrangeNN(container, g2); 103 } 104 else if (h == LengthConstraintType.FIXED) { 105 return arrangeNF(container, g2, constraint); 106 } 107 else if (h == LengthConstraintType.RANGE) { 108 // find optimum height, then map to range 109 return arrangeNR(container, g2, constraint); 110 } 111 } 112 else if (w == LengthConstraintType.FIXED) { 113 if (h == LengthConstraintType.NONE) { 114 // find optimum height 115 return arrangeFN(container, g2, constraint); 116 } 117 else if (h == LengthConstraintType.FIXED) { 118 return arrangeFF(container, g2, constraint); 119 } 120 else if (h == LengthConstraintType.RANGE) { 121 // find optimum height and map to range 122 return arrangeFR(container, g2, constraint); 123 } 124 } 125 else if (w == LengthConstraintType.RANGE) { 126 // find optimum width and map to range 127 if (h == LengthConstraintType.NONE) { 128 // find optimum height 129 return arrangeRN(container, g2, constraint); 130 } 131 else if (h == LengthConstraintType.FIXED) { 132 // fixed width 133 return arrangeRF(container, g2, constraint); 134 } 135 else if (h == LengthConstraintType.RANGE) { 136 return arrangeRR(container, g2, constraint); 137 } 138 } 139 throw new RuntimeException("Should never get to here!"); 140 } 141 142 /** 143 * Arranges the container with no constraint on the width or height. 144 * 145 * @param container the container ({@code null} not permitted). 146 * @param g2 the graphics device. 147 * 148 * @return The size. 149 */ 150 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { 151 double maxW = 0.0; 152 double maxH = 0.0; 153 List blocks = container.getBlocks(); 154 Iterator iterator = blocks.iterator(); 155 while (iterator.hasNext()) { 156 Block b = (Block) iterator.next(); 157 if (b != null) { 158 Size2D s = b.arrange(g2, RectangleConstraint.NONE); 159 maxW = Math.max(maxW, s.width); 160 maxH = Math.max(maxH, s.height); 161 } 162 } 163 double width = this.columns * maxW; 164 double height = this.rows * maxH; 165 RectangleConstraint c = new RectangleConstraint(width, height); 166 return arrangeFF(container, g2, c); 167 } 168 169 /** 170 * Arranges the container with a fixed overall width and height. 171 * 172 * @param container the container ({@code null} not permitted). 173 * @param g2 the graphics device. 174 * @param constraint the constraint ({@code null} not permitted). 175 * 176 * @return The size following the arrangement. 177 */ 178 protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, 179 RectangleConstraint constraint) { 180 double width = constraint.getWidth() / this.columns; 181 double height = constraint.getHeight() / this.rows; 182 List blocks = container.getBlocks(); 183 for (int c = 0; c < this.columns; c++) { 184 for (int r = 0; r < this.rows; r++) { 185 int index = r * this.columns + c; 186 if (index >= blocks.size()) { 187 break; 188 } 189 Block b = (Block) blocks.get(index); 190 if (b != null) { 191 b.setBounds(new Rectangle2D.Double(c * width, r * height, 192 width, height)); 193 } 194 } 195 } 196 return new Size2D(this.columns * width, this.rows * height); 197 } 198 199 /** 200 * Arrange with a fixed width and a height within a given range. 201 * 202 * @param container the container. 203 * @param constraint the constraint. 204 * @param g2 the graphics device. 205 * 206 * @return The size of the arrangement. 207 */ 208 protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, 209 RectangleConstraint constraint) { 210 211 RectangleConstraint c1 = constraint.toUnconstrainedHeight(); 212 Size2D size1 = arrange(container, g2, c1); 213 214 if (constraint.getHeightRange().contains(size1.getHeight())) { 215 return size1; 216 } 217 else { 218 double h = constraint.getHeightRange().constrain(size1.getHeight()); 219 RectangleConstraint c2 = constraint.toFixedHeight(h); 220 return arrange(container, g2, c2); 221 } 222 } 223 224 /** 225 * Arrange with a fixed height and a width within a given range. 226 * 227 * @param container the container. 228 * @param constraint the constraint. 229 * @param g2 the graphics device. 230 * 231 * @return The size of the arrangement. 232 */ 233 protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, 234 RectangleConstraint constraint) { 235 236 RectangleConstraint c1 = constraint.toUnconstrainedWidth(); 237 Size2D size1 = arrange(container, g2, c1); 238 239 if (constraint.getWidthRange().contains(size1.getWidth())) { 240 return size1; 241 } 242 else { 243 double w = constraint.getWidthRange().constrain(size1.getWidth()); 244 RectangleConstraint c2 = constraint.toFixedWidth(w); 245 return arrange(container, g2, c2); 246 } 247 } 248 249 /** 250 * Arrange with a fixed width and no height constraint. 251 * 252 * @param container the container. 253 * @param constraint the constraint. 254 * @param g2 the graphics device. 255 * 256 * @return The size of the arrangement. 257 */ 258 protected Size2D arrangeRN(BlockContainer container, Graphics2D g2, 259 RectangleConstraint constraint) { 260 261 RectangleConstraint c1 = constraint.toUnconstrainedWidth(); 262 Size2D size1 = arrange(container, g2, c1); 263 264 if (constraint.getWidthRange().contains(size1.getWidth())) { 265 return size1; 266 } 267 else { 268 double w = constraint.getWidthRange().constrain(size1.getWidth()); 269 RectangleConstraint c2 = constraint.toFixedWidth(w); 270 return arrange(container, g2, c2); 271 } 272 } 273 274 /** 275 * Arrange with a fixed height and no width constraint. 276 * 277 * @param container the container. 278 * @param constraint the constraint. 279 * @param g2 the graphics device. 280 * 281 * @return The size of the arrangement. 282 */ 283 protected Size2D arrangeNR(BlockContainer container, Graphics2D g2, 284 RectangleConstraint constraint) { 285 286 RectangleConstraint c1 = constraint.toUnconstrainedHeight(); 287 Size2D size1 = arrange(container, g2, c1); 288 289 if (constraint.getHeightRange().contains(size1.getHeight())) { 290 return size1; 291 } 292 else { 293 double h = constraint.getHeightRange().constrain(size1.getHeight()); 294 RectangleConstraint c2 = constraint.toFixedHeight(h); 295 return arrange(container, g2, c2); 296 } 297 } 298 299 /** 300 * Arrange with ranges for both the width and height constraints. 301 * 302 * @param container the container. 303 * @param constraint the constraint. 304 * @param g2 the graphics device. 305 * 306 * @return The size of the arrangement. 307 */ 308 protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, 309 RectangleConstraint constraint) { 310 311 Size2D size1 = arrange(container, g2, RectangleConstraint.NONE); 312 313 if (constraint.getWidthRange().contains(size1.getWidth())) { 314 if (constraint.getHeightRange().contains(size1.getHeight())) { 315 return size1; 316 } 317 else { 318 // width is OK, but height must be constrained 319 double h = constraint.getHeightRange().constrain( 320 size1.getHeight()); 321 RectangleConstraint cc = new RectangleConstraint( 322 size1.getWidth(), h); 323 return arrangeFF(container, g2, cc); 324 } 325 } 326 else { 327 if (constraint.getHeightRange().contains(size1.getHeight())) { 328 // height is OK, but width must be constrained 329 double w = constraint.getWidthRange().constrain( 330 size1.getWidth()); 331 RectangleConstraint cc = new RectangleConstraint(w, 332 size1.getHeight()); 333 return arrangeFF(container, g2, cc); 334 335 } 336 else { 337 double w = constraint.getWidthRange().constrain( 338 size1.getWidth()); 339 double h = constraint.getHeightRange().constrain( 340 size1.getHeight()); 341 RectangleConstraint cc = new RectangleConstraint(w, h); 342 return arrangeFF(container, g2, cc); 343 } 344 } 345 } 346 347 /** 348 * Arrange with a fixed width and a height within a given range. 349 * 350 * @param container the container. 351 * @param g2 the graphics device. 352 * @param constraint the constraint. 353 * 354 * @return The size of the arrangement. 355 */ 356 protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, 357 RectangleConstraint constraint) { 358 359 double width = constraint.getWidth() / this.columns; 360 RectangleConstraint bc = constraint.toFixedWidth(width); 361 List blocks = container.getBlocks(); 362 double maxH = 0.0; 363 for (int r = 0; r < this.rows; r++) { 364 for (int c = 0; c < this.columns; c++) { 365 int index = r * this.columns + c; 366 if (index >= blocks.size()) { 367 break; 368 } 369 Block b = (Block) blocks.get(index); 370 if (b != null) { 371 Size2D s = b.arrange(g2, bc); 372 maxH = Math.max(maxH, s.getHeight()); 373 } 374 } 375 } 376 RectangleConstraint cc = constraint.toFixedHeight(maxH * this.rows); 377 return arrange(container, g2, cc); 378 } 379 380 /** 381 * Arrange with a fixed height and no constraint for the width. 382 * 383 * @param container the container. 384 * @param g2 the graphics device. 385 * @param constraint the constraint. 386 * 387 * @return The size of the arrangement. 388 */ 389 protected Size2D arrangeNF(BlockContainer container, Graphics2D g2, 390 RectangleConstraint constraint) { 391 392 double height = constraint.getHeight() / this.rows; 393 RectangleConstraint bc = constraint.toFixedHeight(height); 394 List blocks = container.getBlocks(); 395 double maxW = 0.0; 396 for (int r = 0; r < this.rows; r++) { 397 for (int c = 0; c < this.columns; c++) { 398 int index = r * this.columns + c; 399 if (index >= blocks.size()) { 400 break; 401 } 402 Block b = (Block) blocks.get(index); 403 if (b != null) { 404 Size2D s = b.arrange(g2, bc); 405 maxW = Math.max(maxW, s.getWidth()); 406 } 407 } 408 } 409 RectangleConstraint cc = constraint.toFixedWidth(maxW * this.columns); 410 return arrange(container, g2, cc); 411 } 412 413 /** 414 * Clears any cached layout information retained by the arrangement. 415 */ 416 @Override 417 public void clear() { 418 // nothing to clear 419 } 420 421 /** 422 * Compares this layout manager for equality with an arbitrary object. 423 * 424 * @param obj the object. 425 * 426 * @return A boolean. 427 */ 428 @Override 429 public boolean equals(Object obj) { 430 if (obj == this) { 431 return true; 432 } 433 if (!(obj instanceof GridArrangement)) { 434 return false; 435 } 436 GridArrangement that = (GridArrangement) obj; 437 if (this.columns != that.columns) { 438 return false; 439 } 440 if (this.rows != that.rows) { 441 return false; 442 } 443 return true; 444 } 445 446 @Override 447 public int hashCode() { 448 int hash = 7; 449 hash = 29 * hash + this.rows; 450 hash = 29 * hash + this.columns; 451 return hash; 452 } 453 454}