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