001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * BorderArrangement.java 029 * ---------------------- 030 * (C) Copyright 2004-2007, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes: 036 * -------- 037 * 22-Oct-2004 : Version 1 (DG); 038 * 08-Feb-2005 : Updated for changes in RectangleConstraint (DG); 039 * 24-Feb-2005 : Improved arrangeRR() method (DG); 040 * 03-May-2005 : Implemented Serializable and added equals() method (DG); 041 * 13-May-2005 : Fixed bugs in the arrange() method (DG); 042 * 043 */ 044 045 package org.jfree.chart.block; 046 047 import java.awt.Graphics2D; 048 import java.awt.geom.Rectangle2D; 049 import java.io.Serializable; 050 051 import org.jfree.data.Range; 052 import org.jfree.ui.RectangleEdge; 053 import org.jfree.ui.Size2D; 054 import org.jfree.util.ObjectUtilities; 055 056 /** 057 * An arrangement manager that lays out blocks in a similar way to 058 * Swing's BorderLayout class. 059 */ 060 public class BorderArrangement implements Arrangement, Serializable { 061 062 /** For serialization. */ 063 private static final long serialVersionUID = 506071142274883745L; 064 065 /** The block (if any) at the center of the layout. */ 066 private Block centerBlock; 067 068 /** The block (if any) at the top of the layout. */ 069 private Block topBlock; 070 071 /** The block (if any) at the bottom of the layout. */ 072 private Block bottomBlock; 073 074 /** The block (if any) at the left of the layout. */ 075 private Block leftBlock; 076 077 /** The block (if any) at the right of the layout. */ 078 private Block rightBlock; 079 080 /** 081 * Creates a new instance. 082 */ 083 public BorderArrangement() { 084 } 085 086 /** 087 * Adds a block to the arrangement manager at the specified edge. 088 * 089 * @param block the block (<code>null</code> permitted). 090 * @param key the edge (an instance of {@link RectangleEdge}) or 091 * <code>null</code> for the center block. 092 */ 093 public void add(Block block, Object key) { 094 095 if (key == null) { 096 this.centerBlock = block; 097 } 098 else { 099 RectangleEdge edge = (RectangleEdge) key; 100 if (edge == RectangleEdge.TOP) { 101 this.topBlock = block; 102 } 103 else if (edge == RectangleEdge.BOTTOM) { 104 this.bottomBlock = block; 105 } 106 else if (edge == RectangleEdge.LEFT) { 107 this.leftBlock = block; 108 } 109 else if (edge == RectangleEdge.RIGHT) { 110 this.rightBlock = block; 111 } 112 } 113 } 114 115 /** 116 * Arranges the items in the specified container, subject to the given 117 * constraint. 118 * 119 * @param container the container. 120 * @param g2 the graphics device. 121 * @param constraint the constraint. 122 * 123 * @return The block size. 124 */ 125 public Size2D arrange(BlockContainer container, 126 Graphics2D g2, 127 RectangleConstraint constraint) { 128 RectangleConstraint contentConstraint 129 = container.toContentConstraint(constraint); 130 Size2D contentSize = null; 131 LengthConstraintType w = contentConstraint.getWidthConstraintType(); 132 LengthConstraintType h = contentConstraint.getHeightConstraintType(); 133 if (w == LengthConstraintType.NONE) { 134 if (h == LengthConstraintType.NONE) { 135 contentSize = arrangeNN(container, g2); 136 } 137 else if (h == LengthConstraintType.FIXED) { 138 throw new RuntimeException("Not implemented."); 139 } 140 else if (h == LengthConstraintType.RANGE) { 141 throw new RuntimeException("Not implemented."); 142 } 143 } 144 else if (w == LengthConstraintType.FIXED) { 145 if (h == LengthConstraintType.NONE) { 146 contentSize = arrangeFN(container, g2, constraint.getWidth()); 147 } 148 else if (h == LengthConstraintType.FIXED) { 149 contentSize = arrangeFF(container, g2, constraint); 150 } 151 else if (h == LengthConstraintType.RANGE) { 152 contentSize = arrangeFR(container, g2, constraint); 153 } 154 } 155 else if (w == LengthConstraintType.RANGE) { 156 if (h == LengthConstraintType.NONE) { 157 throw new RuntimeException("Not implemented."); 158 } 159 else if (h == LengthConstraintType.FIXED) { 160 throw new RuntimeException("Not implemented."); 161 } 162 else if (h == LengthConstraintType.RANGE) { 163 contentSize = arrangeRR(container, constraint.getWidthRange(), 164 constraint.getHeightRange(), g2); 165 } 166 } 167 return new Size2D(container.calculateTotalWidth(contentSize.getWidth()), 168 container.calculateTotalHeight(contentSize.getHeight())); 169 } 170 171 /** 172 * Performs an arrangement without constraints. 173 * 174 * @param container the container. 175 * @param g2 the graphics device. 176 * 177 * @return The container size after the arrangement. 178 */ 179 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { 180 double[] w = new double[5]; 181 double[] h = new double[5]; 182 if (this.topBlock != null) { 183 Size2D size = this.topBlock.arrange(g2, RectangleConstraint.NONE); 184 w[0] = size.width; 185 h[0] = size.height; 186 } 187 if (this.bottomBlock != null) { 188 Size2D size = this.bottomBlock.arrange(g2, 189 RectangleConstraint.NONE); 190 w[1] = size.width; 191 h[1] = size.height; 192 } 193 if (this.leftBlock != null) { 194 Size2D size = this.leftBlock.arrange(g2, RectangleConstraint.NONE); 195 w[2] = size.width; 196 h[2] = size.height; 197 } 198 if (this.rightBlock != null) { 199 Size2D size = this.rightBlock.arrange(g2, RectangleConstraint.NONE); 200 w[3] = size.width; 201 h[3] = size.height; 202 } 203 204 h[2] = Math.max(h[2], h[3]); 205 h[3] = h[2]; 206 207 if (this.centerBlock != null) { 208 Size2D size = this.centerBlock.arrange(g2, 209 RectangleConstraint.NONE); 210 w[4] = size.width; 211 h[4] = size.height; 212 } 213 double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3])); 214 double centerHeight = Math.max(h[2], Math.max(h[3], h[4])); 215 double height = h[0] + h[1] + centerHeight; 216 if (this.topBlock != null) { 217 this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width, 218 h[0])); 219 } 220 if (this.bottomBlock != null) { 221 this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, 222 height - h[1], width, h[1])); 223 } 224 if (this.leftBlock != null) { 225 this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2], 226 centerHeight)); 227 } 228 if (this.rightBlock != null) { 229 this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3], 230 h[0], w[3], centerHeight)); 231 } 232 233 if (this.centerBlock != null) { 234 this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], 235 width - w[2] - w[3], centerHeight)); 236 } 237 return new Size2D(width, height); 238 } 239 240 /** 241 * Performs an arrangement with a fixed width and a range for the height. 242 * 243 * @param container the container. 244 * @param g2 the graphics device. 245 * @param constraint the constraint. 246 * 247 * @return The container size after the arrangement. 248 */ 249 protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, 250 RectangleConstraint constraint) { 251 Size2D size1 = arrangeFN(container, g2, constraint.getWidth()); 252 if (constraint.getHeightRange().contains(size1.getHeight())) { 253 return size1; 254 } 255 else { 256 double h = constraint.getHeightRange().constrain(size1.getHeight()); 257 RectangleConstraint c2 = constraint.toFixedHeight(h); 258 return arrange(container, g2, c2); 259 } 260 } 261 262 /** 263 * Arranges the container width a fixed width and no constraint on the 264 * height. 265 * 266 * @param container the container. 267 * @param g2 the graphics device. 268 * @param width the fixed width. 269 * 270 * @return The container size after arranging the contents. 271 */ 272 protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, 273 double width) { 274 double[] w = new double[5]; 275 double[] h = new double[5]; 276 RectangleConstraint c1 = new RectangleConstraint(width, null, 277 LengthConstraintType.FIXED, 0.0, null, 278 LengthConstraintType.NONE); 279 if (this.topBlock != null) { 280 Size2D size = this.topBlock.arrange(g2, c1); 281 w[0] = size.width; 282 h[0] = size.height; 283 } 284 if (this.bottomBlock != null) { 285 Size2D size = this.bottomBlock.arrange(g2, c1); 286 w[1] = size.width; 287 h[1] = size.height; 288 } 289 RectangleConstraint c2 = new RectangleConstraint(0.0, 290 new Range(0.0, width), LengthConstraintType.RANGE, 291 0.0, null, LengthConstraintType.NONE); 292 if (this.leftBlock != null) { 293 Size2D size = this.leftBlock.arrange(g2, c2); 294 w[2] = size.width; 295 h[2] = size.height; 296 } 297 if (this.rightBlock != null) { 298 double maxW = Math.max(width - w[2], 0.0); 299 RectangleConstraint c3 = new RectangleConstraint(0.0, 300 new Range(Math.min(w[2], maxW), maxW), 301 LengthConstraintType.RANGE, 0.0, null, 302 LengthConstraintType.NONE); 303 Size2D size = this.rightBlock.arrange(g2, c3); 304 w[3] = size.width; 305 h[3] = size.height; 306 } 307 308 h[2] = Math.max(h[2], h[3]); 309 h[3] = h[2]; 310 311 if (this.centerBlock != null) { 312 RectangleConstraint c4 = new RectangleConstraint(width - w[2] 313 - w[3], null, LengthConstraintType.FIXED, 0.0, null, 314 LengthConstraintType.NONE); 315 Size2D size = this.centerBlock.arrange(g2, c4); 316 w[4] = size.width; 317 h[4] = size.height; 318 } 319 double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4])); 320 return arrange(container, g2, new RectangleConstraint(width, height)); 321 } 322 323 /** 324 * Performs an arrangement with range constraints on both the vertical 325 * and horizontal sides. 326 * 327 * @param container the container. 328 * @param widthRange the allowable range for the container width. 329 * @param heightRange the allowable range for the container height. 330 * @param g2 the graphics device. 331 * 332 * @return The container size. 333 */ 334 protected Size2D arrangeRR(BlockContainer container, 335 Range widthRange, Range heightRange, 336 Graphics2D g2) { 337 double[] w = new double[5]; 338 double[] h = new double[5]; 339 if (this.topBlock != null) { 340 RectangleConstraint c1 = new RectangleConstraint(widthRange, 341 heightRange); 342 Size2D size = this.topBlock.arrange(g2, c1); 343 w[0] = size.width; 344 h[0] = size.height; 345 } 346 if (this.bottomBlock != null) { 347 Range heightRange2 = Range.shift(heightRange, -h[0], false); 348 RectangleConstraint c2 = new RectangleConstraint(widthRange, 349 heightRange2); 350 Size2D size = this.bottomBlock.arrange(g2, c2); 351 w[1] = size.width; 352 h[1] = size.height; 353 } 354 Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1])); 355 if (this.leftBlock != null) { 356 RectangleConstraint c3 = new RectangleConstraint(widthRange, 357 heightRange3); 358 Size2D size = this.leftBlock.arrange(g2, c3); 359 w[2] = size.width; 360 h[2] = size.height; 361 } 362 Range widthRange2 = Range.shift(widthRange, -w[2], false); 363 if (this.rightBlock != null) { 364 RectangleConstraint c4 = new RectangleConstraint(widthRange2, 365 heightRange3); 366 Size2D size = this.rightBlock.arrange(g2, c4); 367 w[3] = size.width; 368 h[3] = size.height; 369 } 370 371 h[2] = Math.max(h[2], h[3]); 372 h[3] = h[2]; 373 Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]), false); 374 if (this.centerBlock != null) { 375 RectangleConstraint c5 = new RectangleConstraint(widthRange3, 376 heightRange3); 377 // TODO: the width and height ranges should be reduced by the 378 // height required for the top and bottom, and the width required 379 // by the left and right 380 Size2D size = this.centerBlock.arrange(g2, c5); 381 w[4] = size.width; 382 h[4] = size.height; 383 } 384 double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3])); 385 double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4])); 386 if (this.topBlock != null) { 387 this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width, 388 h[0])); 389 } 390 if (this.bottomBlock != null) { 391 this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, 392 height - h[1], width, h[1])); 393 } 394 if (this.leftBlock != null) { 395 this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2], 396 h[2])); 397 } 398 if (this.rightBlock != null) { 399 this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3], 400 h[0], w[3], h[3])); 401 } 402 403 if (this.centerBlock != null) { 404 this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], 405 width - w[2] - w[3], height - h[0] - h[1])); 406 } 407 return new Size2D(width, height); 408 } 409 410 /** 411 * Arranges the items within a container. 412 * 413 * @param container the container. 414 * @param constraint the constraint. 415 * @param g2 the graphics device. 416 * 417 * @return The container size after the arrangement. 418 */ 419 protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, 420 RectangleConstraint constraint) { 421 double[] w = new double[5]; 422 double[] h = new double[5]; 423 w[0] = constraint.getWidth(); 424 if (this.topBlock != null) { 425 RectangleConstraint c1 = new RectangleConstraint(w[0], null, 426 LengthConstraintType.FIXED, 0.0, 427 new Range(0.0, constraint.getHeight()), 428 LengthConstraintType.RANGE); 429 Size2D size = this.topBlock.arrange(g2, c1); 430 h[0] = size.height; 431 } 432 w[1] = w[0]; 433 if (this.bottomBlock != null) { 434 RectangleConstraint c2 = new RectangleConstraint(w[0], null, 435 LengthConstraintType.FIXED, 0.0, new Range(0.0, 436 constraint.getHeight() - h[0]), LengthConstraintType.RANGE); 437 Size2D size = this.bottomBlock.arrange(g2, c2); 438 h[1] = size.height; 439 } 440 h[2] = constraint.getHeight() - h[1] - h[0]; 441 if (this.leftBlock != null) { 442 RectangleConstraint c3 = new RectangleConstraint(0.0, 443 new Range(0.0, constraint.getWidth()), 444 LengthConstraintType.RANGE, h[2], null, 445 LengthConstraintType.FIXED); 446 Size2D size = this.leftBlock.arrange(g2, c3); 447 w[2] = size.width; 448 } 449 h[3] = h[2]; 450 if (this.rightBlock != null) { 451 RectangleConstraint c4 = new RectangleConstraint(0.0, 452 new Range(0.0, constraint.getWidth() - w[2]), 453 LengthConstraintType.RANGE, h[2], null, 454 LengthConstraintType.FIXED); 455 Size2D size = this.rightBlock.arrange(g2, c4); 456 w[3] = size.width; 457 } 458 h[4] = h[2]; 459 w[4] = constraint.getWidth() - w[3] - w[2]; 460 RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]); 461 if (this.centerBlock != null) { 462 this.centerBlock.arrange(g2, c5); 463 } 464 465 if (this.topBlock != null) { 466 this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, w[0], 467 h[0])); 468 } 469 if (this.bottomBlock != null) { 470 this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, h[0] + h[2], 471 w[1], h[1])); 472 } 473 if (this.leftBlock != null) { 474 this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2], 475 h[2])); 476 } 477 if (this.rightBlock != null) { 478 this.rightBlock.setBounds(new Rectangle2D.Double(w[2] + w[4], h[0], 479 w[3], h[3])); 480 } 481 if (this.centerBlock != null) { 482 this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], w[4], 483 h[4])); 484 } 485 return new Size2D(constraint.getWidth(), constraint.getHeight()); 486 } 487 488 /** 489 * Clears the layout. 490 */ 491 public void clear() { 492 this.centerBlock = null; 493 this.topBlock = null; 494 this.bottomBlock = null; 495 this.leftBlock = null; 496 this.rightBlock = null; 497 } 498 499 /** 500 * Tests this arrangement for equality with an arbitrary object. 501 * 502 * @param obj the object (<code>null</code> permitted). 503 * 504 * @return A boolean. 505 */ 506 public boolean equals(Object obj) { 507 if (obj == this) { 508 return true; 509 } 510 if (!(obj instanceof BorderArrangement)) { 511 return false; 512 } 513 BorderArrangement that = (BorderArrangement) obj; 514 if (!ObjectUtilities.equal(this.topBlock, that.topBlock)) { 515 return false; 516 } 517 if (!ObjectUtilities.equal(this.bottomBlock, that.bottomBlock)) { 518 return false; 519 } 520 if (!ObjectUtilities.equal(this.leftBlock, that.leftBlock)) { 521 return false; 522 } 523 if (!ObjectUtilities.equal(this.rightBlock, that.rightBlock)) { 524 return false; 525 } 526 if (!ObjectUtilities.equal(this.centerBlock, that.centerBlock)) { 527 return false; 528 } 529 return true; 530 } 531 }