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