Source for org.jfree.chart.block.BorderArrangement

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
  22:  * USA.
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * ----------------------
  28:  * BorderArrangement.java
  29:  * ----------------------
  30:  * (C) Copyright 2004-2008, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * Changes:
  36:  * --------
  37:  * 22-Oct-2004 : Version 1 (DG);
  38:  * 08-Feb-2005 : Updated for changes in RectangleConstraint (DG);
  39:  * 24-Feb-2005 : Improved arrangeRR() method (DG);
  40:  * 03-May-2005 : Implemented Serializable and added equals() method (DG);
  41:  * 13-May-2005 : Fixed bugs in the arrange() method (DG);
  42:  * 08-Apr-2008 : Fixed bug in arrangeFF() method where width is too small for
  43:  *               left and right blocks (DG);
  44:  *
  45:  */
  46: 
  47: package org.jfree.chart.block;
  48: 
  49: import java.awt.Graphics2D;
  50: import java.awt.geom.Rectangle2D;
  51: import java.io.Serializable;
  52: 
  53: import org.jfree.data.Range;
  54: import org.jfree.ui.RectangleEdge;
  55: import org.jfree.ui.Size2D;
  56: import org.jfree.util.ObjectUtilities;
  57: 
  58: /**
  59:  * An arrangement manager that lays out blocks in a similar way to
  60:  * Swing's BorderLayout class.
  61:  */
  62: public class BorderArrangement implements Arrangement, Serializable {
  63: 
  64:     /** For serialization. */
  65:     private static final long serialVersionUID = 506071142274883745L;
  66: 
  67:     /** The block (if any) at the center of the layout. */
  68:     private Block centerBlock;
  69: 
  70:     /** The block (if any) at the top of the layout. */
  71:     private Block topBlock;
  72: 
  73:     /** The block (if any) at the bottom of the layout. */
  74:     private Block bottomBlock;
  75: 
  76:     /** The block (if any) at the left of the layout. */
  77:     private Block leftBlock;
  78: 
  79:     /** The block (if any) at the right of the layout. */
  80:     private Block rightBlock;
  81: 
  82:     /**
  83:      * Creates a new instance.
  84:      */
  85:     public BorderArrangement() {
  86:     }
  87: 
  88:     /**
  89:      * Adds a block to the arrangement manager at the specified edge.
  90:      *
  91:      * @param block  the block (<code>null</code> permitted).
  92:      * @param key  the edge (an instance of {@link RectangleEdge}) or
  93:      *             <code>null</code> for the center block.
  94:      */
  95:     public void add(Block block, Object key) {
  96: 
  97:         if (key == null) {
  98:             this.centerBlock = block;
  99:         }
 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(container, constraint.getWidthRange(),
 166:                         constraint.getHeightRange(), g2);
 167:             }
 168:         }
 169:         return new Size2D(container.calculateTotalWidth(contentSize.getWidth()),
 170:                 container.calculateTotalHeight(contentSize.getHeight()));
 171:     }
 172: 
 173:     /**
 174:      * Performs an arrangement without constraints.
 175:      *
 176:      * @param container  the container.
 177:      * @param g2  the graphics device.
 178:      *
 179:      * @return The container size after the arrangement.
 180:      */
 181:     protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
 182:         double[] w = new double[5];
 183:         double[] h = new double[5];
 184:         if (this.topBlock != null) {
 185:             Size2D size = this.topBlock.arrange(g2, RectangleConstraint.NONE);
 186:             w[0] = size.width;
 187:             h[0] = size.height;
 188:         }
 189:         if (this.bottomBlock != null) {
 190:             Size2D size = this.bottomBlock.arrange(g2,
 191:                     RectangleConstraint.NONE);
 192:             w[1] = size.width;
 193:             h[1] = size.height;
 194:         }
 195:         if (this.leftBlock != null) {
 196:             Size2D size = this.leftBlock.arrange(g2, RectangleConstraint.NONE);
 197:             w[2] = size.width;
 198:             h[2] = size.height;
 199:        }
 200:         if (this.rightBlock != null) {
 201:             Size2D size = this.rightBlock.arrange(g2, RectangleConstraint.NONE);
 202:             w[3] = size.width;
 203:             h[3] = size.height;
 204:         }
 205: 
 206:         h[2] = Math.max(h[2], h[3]);
 207:         h[3] = h[2];
 208: 
 209:         if (this.centerBlock != null) {
 210:             Size2D size = this.centerBlock.arrange(g2,
 211:                     RectangleConstraint.NONE);
 212:             w[4] = size.width;
 213:             h[4] = size.height;
 214:         }
 215:         double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
 216:         double centerHeight = Math.max(h[2], Math.max(h[3], h[4]));
 217:         double height = h[0] + h[1] + centerHeight;
 218:         if (this.topBlock != null) {
 219:             this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width,
 220:                     h[0]));
 221:         }
 222:         if (this.bottomBlock != null) {
 223:             this.bottomBlock.setBounds(new Rectangle2D.Double(0.0,
 224:                     height - h[1], width, h[1]));
 225:         }
 226:         if (this.leftBlock != null) {
 227:             this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2],
 228:                     centerHeight));
 229:         }
 230:         if (this.rightBlock != null) {
 231:             this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3],
 232:                     h[0], w[3], centerHeight));
 233:         }
 234: 
 235:         if (this.centerBlock != null) {
 236:             this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0],
 237:                     width - w[2] - w[3], centerHeight));
 238:         }
 239:         return new Size2D(width, height);
 240:     }
 241: 
 242:     /**
 243:      * Performs an arrangement with a fixed width and a range for the height.
 244:      *
 245:      * @param container  the container.
 246:      * @param g2  the graphics device.
 247:      * @param constraint  the constraint.
 248:      *
 249:      * @return The container size after the arrangement.
 250:      */
 251:     protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
 252:                                RectangleConstraint constraint) {
 253:         Size2D size1 = arrangeFN(container, g2, constraint.getWidth());
 254:         if (constraint.getHeightRange().contains(size1.getHeight())) {
 255:             return size1;
 256:         }
 257:         else {
 258:             double h = constraint.getHeightRange().constrain(size1.getHeight());
 259:             RectangleConstraint c2 = constraint.toFixedHeight(h);
 260:             return arrange(container, g2, c2);
 261:         }
 262:     }
 263: 
 264:     /**
 265:      * Arranges the container width a fixed width and no constraint on the
 266:      * height.
 267:      *
 268:      * @param container  the container.
 269:      * @param g2  the graphics device.
 270:      * @param width  the fixed width.
 271:      *
 272:      * @return The container size after arranging the contents.
 273:      */
 274:     protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
 275:                                double width) {
 276:         double[] w = new double[5];
 277:         double[] h = new double[5];
 278:         RectangleConstraint c1 = new RectangleConstraint(width, null,
 279:                 LengthConstraintType.FIXED, 0.0, null,
 280:                 LengthConstraintType.NONE);
 281:         if (this.topBlock != null) {
 282:             Size2D size = this.topBlock.arrange(g2, c1);
 283:             w[0] = size.width;
 284:             h[0] = size.height;
 285:         }
 286:         if (this.bottomBlock != null) {
 287:             Size2D size = this.bottomBlock.arrange(g2, c1);
 288:             w[1] = size.width;
 289:             h[1] = size.height;
 290:         }
 291:         RectangleConstraint c2 = new RectangleConstraint(0.0,
 292:                 new Range(0.0, width), LengthConstraintType.RANGE,
 293:                 0.0, null, LengthConstraintType.NONE);
 294:         if (this.leftBlock != null) {
 295:             Size2D size = this.leftBlock.arrange(g2, c2);
 296:             w[2] = size.width;
 297:             h[2] = size.height;
 298:         }
 299:         if (this.rightBlock != null) {
 300:             double maxW = Math.max(width - w[2], 0.0);
 301:             RectangleConstraint c3 = new RectangleConstraint(0.0,
 302:                     new Range(Math.min(w[2], maxW), maxW),
 303:                     LengthConstraintType.RANGE, 0.0, null,
 304:                     LengthConstraintType.NONE);
 305:             Size2D size = this.rightBlock.arrange(g2, c3);
 306:             w[3] = size.width;
 307:             h[3] = size.height;
 308:         }
 309: 
 310:         h[2] = Math.max(h[2], h[3]);
 311:         h[3] = h[2];
 312: 
 313:         if (this.centerBlock != null) {
 314:             RectangleConstraint c4 = new RectangleConstraint(width - w[2]
 315:                     - w[3], null, LengthConstraintType.FIXED, 0.0, null,
 316:                     LengthConstraintType.NONE);
 317:             Size2D size = this.centerBlock.arrange(g2, c4);
 318:             w[4] = size.width;
 319:             h[4] = size.height;
 320:         }
 321:         double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
 322:         return arrange(container, g2, new RectangleConstraint(width, height));
 323:     }
 324: 
 325:     /**
 326:      * Performs an arrangement with range constraints on both the vertical
 327:      * and horizontal sides.
 328:      *
 329:      * @param container  the container.
 330:      * @param widthRange  the allowable range for the container width.
 331:      * @param heightRange  the allowable range for the container height.
 332:      * @param g2  the graphics device.
 333:      *
 334:      * @return The container size.
 335:      */
 336:     protected Size2D arrangeRR(BlockContainer container,
 337:                                Range widthRange, Range heightRange,
 338:                                Graphics2D g2) {
 339:         double[] w = new double[5];
 340:         double[] h = new double[5];
 341:         if (this.topBlock != null) {
 342:             RectangleConstraint c1 = new RectangleConstraint(widthRange,
 343:                     heightRange);
 344:             Size2D size = this.topBlock.arrange(g2, c1);
 345:             w[0] = size.width;
 346:             h[0] = size.height;
 347:         }
 348:         if (this.bottomBlock != null) {
 349:             Range heightRange2 = Range.shift(heightRange, -h[0], false);
 350:             RectangleConstraint c2 = new RectangleConstraint(widthRange,
 351:                     heightRange2);
 352:             Size2D size = this.bottomBlock.arrange(g2, c2);
 353:             w[1] = size.width;
 354:             h[1] = size.height;
 355:         }
 356:         Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1]));
 357:         if (this.leftBlock != null) {
 358:             RectangleConstraint c3 = new RectangleConstraint(widthRange,
 359:                     heightRange3);
 360:             Size2D size = this.leftBlock.arrange(g2, c3);
 361:             w[2] = size.width;
 362:             h[2] = size.height;
 363:         }
 364:         Range widthRange2 = Range.shift(widthRange, -w[2], false);
 365:         if (this.rightBlock != null) {
 366:             RectangleConstraint c4 = new RectangleConstraint(widthRange2,
 367:                     heightRange3);
 368:             Size2D size = this.rightBlock.arrange(g2, c4);
 369:             w[3] = size.width;
 370:             h[3] = size.height;
 371:         }
 372: 
 373:         h[2] = Math.max(h[2], h[3]);
 374:         h[3] = h[2];
 375:         Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]), false);
 376:         if (this.centerBlock != null) {
 377:             RectangleConstraint c5 = new RectangleConstraint(widthRange3,
 378:                     heightRange3);
 379:             Size2D size = this.centerBlock.arrange(g2, c5);
 380:             w[4] = size.width;
 381:             h[4] = size.height;
 382:         }
 383:         double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
 384:         double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
 385:         if (this.topBlock != null) {
 386:             this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width,
 387:                     h[0]));
 388:         }
 389:         if (this.bottomBlock != null) {
 390:             this.bottomBlock.setBounds(new Rectangle2D.Double(0.0,
 391:                     height - h[1], width, h[1]));
 392:         }
 393:         if (this.leftBlock != null) {
 394:             this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2],
 395:                     h[2]));
 396:         }
 397:         if (this.rightBlock != null) {
 398:             this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3],
 399:                     h[0], w[3], h[3]));
 400:         }
 401: 
 402:         if (this.centerBlock != null) {
 403:             this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0],
 404:                     width - w[2] - w[3], height - h[0] - h[1]));
 405:         }
 406:         return new Size2D(width, height);
 407:     }
 408: 
 409:     /**
 410:      * Arranges the items within a container.
 411:      *
 412:      * @param container  the container.
 413:      * @param constraint  the constraint.
 414:      * @param g2  the graphics device.
 415:      *
 416:      * @return The container size after the arrangement.
 417:      */
 418:     protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
 419:                                RectangleConstraint constraint) {
 420:         double[] w = new double[5];
 421:         double[] h = new double[5];
 422:         w[0] = constraint.getWidth();
 423:         if (this.topBlock != null) {
 424:             RectangleConstraint c1 = new RectangleConstraint(w[0], null,
 425:                     LengthConstraintType.FIXED, 0.0,
 426:                     new Range(0.0, constraint.getHeight()),
 427:                     LengthConstraintType.RANGE);
 428:             Size2D size = this.topBlock.arrange(g2, c1);
 429:             h[0] = size.height;
 430:         }
 431:         w[1] = w[0];
 432:         if (this.bottomBlock != null) {
 433:             RectangleConstraint c2 = new RectangleConstraint(w[0], null,
 434:                     LengthConstraintType.FIXED, 0.0, new Range(0.0,
 435:                     constraint.getHeight() - h[0]), LengthConstraintType.RANGE);
 436:             Size2D size = this.bottomBlock.arrange(g2, c2);
 437:             h[1] = size.height;
 438:         }
 439:         h[2] = constraint.getHeight() - h[1] - h[0];
 440:         if (this.leftBlock != null) {
 441:             RectangleConstraint c3 = new RectangleConstraint(0.0,
 442:                     new Range(0.0, constraint.getWidth()),
 443:                     LengthConstraintType.RANGE, h[2], null,
 444:                     LengthConstraintType.FIXED);
 445:             Size2D size = this.leftBlock.arrange(g2, c3);
 446:             w[2] = size.width;
 447:         }
 448:         h[3] = h[2];
 449:         if (this.rightBlock != null) {
 450:             RectangleConstraint c4 = new RectangleConstraint(0.0,
 451:                     new Range(0.0, Math.max(constraint.getWidth() - w[2], 0.0)),
 452:                     LengthConstraintType.RANGE, h[2], null,
 453:                     LengthConstraintType.FIXED);
 454:             Size2D size = this.rightBlock.arrange(g2, c4);
 455:             w[3] = size.width;
 456:         }
 457:         h[4] = h[2];
 458:         w[4] = constraint.getWidth() - w[3] - w[2];
 459:         RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]);
 460:         if (this.centerBlock != null) {
 461:             this.centerBlock.arrange(g2, c5);
 462:         }
 463: 
 464:         if (this.topBlock != null) {
 465:             this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, w[0],
 466:                     h[0]));
 467:         }
 468:         if (this.bottomBlock != null) {
 469:             this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, h[0] + h[2],
 470:                     w[1], h[1]));
 471:         }
 472:         if (this.leftBlock != null) {
 473:             this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2],
 474:                     h[2]));
 475:         }
 476:         if (this.rightBlock != null) {
 477:             this.rightBlock.setBounds(new Rectangle2D.Double(w[2] + w[4], h[0],
 478:                     w[3], h[3]));
 479:         }
 480:         if (this.centerBlock != null) {
 481:             this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], w[4],
 482:                     h[4]));
 483:         }
 484:         return new Size2D(constraint.getWidth(), constraint.getHeight());
 485:     }
 486: 
 487:     /**
 488:      * Clears the layout.
 489:      */
 490:     public void clear() {
 491:         this.centerBlock = null;
 492:         this.topBlock = null;
 493:         this.bottomBlock = null;
 494:         this.leftBlock = null;
 495:         this.rightBlock = null;
 496:     }
 497: 
 498:     /**
 499:      * Tests this arrangement for equality with an arbitrary object.
 500:      *
 501:      * @param obj  the object (<code>null</code> permitted).
 502:      *
 503:      * @return A boolean.
 504:      */
 505:     public boolean equals(Object obj) {
 506:         if (obj == this) {
 507:             return true;
 508:         }
 509:         if (!(obj instanceof BorderArrangement)) {
 510:             return false;
 511:         }
 512:         BorderArrangement that = (BorderArrangement) obj;
 513:         if (!ObjectUtilities.equal(this.topBlock, that.topBlock)) {
 514:             return false;
 515:         }
 516:         if (!ObjectUtilities.equal(this.bottomBlock, that.bottomBlock)) {
 517:             return false;
 518:         }
 519:         if (!ObjectUtilities.equal(this.leftBlock, that.leftBlock)) {
 520:             return false;
 521:         }
 522:         if (!ObjectUtilities.equal(this.rightBlock, that.rightBlock)) {
 523:             return false;
 524:         }
 525:         if (!ObjectUtilities.equal(this.centerBlock, that.centerBlock)) {
 526:             return false;
 527:         }
 528:         return true;
 529:     }
 530: }