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