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-2007, 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-2007, 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:  * 
  43:  */
  44: 
  45: package org.jfree.chart.block;
  46: 
  47: import java.awt.Graphics2D;
  48: import java.awt.geom.Rectangle2D;
  49: import java.io.Serializable;
  50: 
  51: import org.jfree.data.Range;
  52: import org.jfree.ui.RectangleEdge;
  53: import org.jfree.ui.Size2D;
  54: import org.jfree.util.ObjectUtilities;
  55: 
  56: /**
  57:  * An arrangement manager that lays out blocks in a similar way to
  58:  * Swing's BorderLayout class.
  59:  */
  60: public class BorderArrangement implements Arrangement, Serializable {
  61:     
  62:     /** For serialization. */
  63:     private static final long serialVersionUID = 506071142274883745L;
  64:     
  65:     /** The block (if any) at the center of the layout. */
  66:     private Block centerBlock;
  67: 
  68:     /** The block (if any) at the top of the layout. */
  69:     private Block topBlock;
  70:     
  71:     /** The block (if any) at the bottom of the layout. */
  72:     private Block bottomBlock;
  73:     
  74:     /** The block (if any) at the left of the layout. */
  75:     private Block leftBlock;
  76:     
  77:     /** The block (if any) at the right of the layout. */
  78:     private Block rightBlock;
  79:     
  80:     /**
  81:      * Creates a new instance.
  82:      */
  83:     public BorderArrangement() {
  84:     }
  85:     
  86:     /**
  87:      * Adds a block to the arrangement manager at the specified edge.
  88:      * 
  89:      * @param block  the block (<code>null</code> permitted).
  90:      * @param key  the edge (an instance of {@link RectangleEdge}) or 
  91:      *             <code>null</code> for the center block.
  92:      */
  93:     public void add(Block block, Object key) {
  94:         
  95:         if (key == null) {
  96:             this.centerBlock = block;
  97:         }
  98:         else {
  99:             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(
 164:                     container, constraint.getWidthRange(),
 165:                     constraint.getHeightRange(), g2
 166:                 );  
 167:             }
 168:         }
 169:         return new Size2D(
 170:             container.calculateTotalWidth(contentSize.getWidth()),
 171:             container.calculateTotalHeight(contentSize.getHeight())
 172:         );
 173:     }
 174:     
 175:     /**
 176:      * Performs an arrangement without constraints.
 177:      * 
 178:      * @param container  the container.
 179:      * @param g2  the graphics device.
 180:      * 
 181:      * @return The container size after the arrangement.
 182:      */
 183:     protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
 184:         double[] w = new double[5];
 185:         double[] h = new double[5];
 186:         if (this.topBlock != null) {
 187:             Size2D size = this.topBlock.arrange(
 188:                 g2, RectangleConstraint.NONE
 189:             );
 190:             w[0] = size.width;
 191:             h[0] = size.height;
 192:         }
 193:         if (this.bottomBlock != null) {
 194:             Size2D size = this.bottomBlock.arrange(
 195:                 g2, RectangleConstraint.NONE
 196:             );
 197:             w[1] = size.width;
 198:             h[1] = size.height;
 199:         }
 200:         if (this.leftBlock != null) {
 201:             Size2D size = this.leftBlock.arrange(
 202:                 g2, RectangleConstraint.NONE
 203:             );
 204:             w[2] = size.width;
 205:             h[2] = size.height;
 206:        }
 207:         if (this.rightBlock != null) {
 208:             Size2D size = this.rightBlock.arrange(
 209:                 g2, RectangleConstraint.NONE
 210:             );
 211:             w[3] = size.width;
 212:             h[3] = size.height;
 213:         }
 214:         
 215:         h[2] = Math.max(h[2], h[3]);
 216:         h[3] = h[2];
 217:         
 218:         if (this.centerBlock != null) {
 219:             Size2D size = this.centerBlock.arrange(
 220:                 g2, RectangleConstraint.NONE
 221:             );
 222:             w[4] = size.width;
 223:             h[4] = size.height;
 224:         }
 225:         double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
 226:         double centerHeight = Math.max(h[2], Math.max(h[3], h[4]));
 227:         double height = h[0] + h[1] + centerHeight;
 228:         if (this.topBlock != null) {
 229:             this.topBlock.setBounds(
 230:                 new Rectangle2D.Double(0.0, 0.0, width, h[0])
 231:             );
 232:         }
 233:         if (this.bottomBlock != null) {
 234:             this.bottomBlock.setBounds(
 235:                 new Rectangle2D.Double(0.0, height - h[1], width, h[1])
 236:             );
 237:         }
 238:         if (this.leftBlock != null) {
 239:             this.leftBlock.setBounds(
 240:                 new Rectangle2D.Double(0.0, h[0], w[2], centerHeight)
 241:             );
 242:         }
 243:         if (this.rightBlock != null) {
 244:             this.rightBlock.setBounds(
 245:                 new Rectangle2D.Double(width - w[3], h[0], w[3], centerHeight)
 246:             );
 247:         }
 248:         
 249:         if (this.centerBlock != null) {
 250:             this.centerBlock.setBounds(
 251:                 new Rectangle2D.Double(
 252:                     w[2], h[0], width - w[2] - w[3], centerHeight
 253:                 )
 254:             );
 255:         }
 256:         return new Size2D(width, height);
 257:     }
 258: 
 259:     /**
 260:      * Performs an arrangement with a fixed width and a range for the height.
 261:      * 
 262:      * @param container  the container.
 263:      * @param g2  the graphics device.
 264:      * @param constraint  the constraint.
 265:      * 
 266:      * @return The container size after the arrangement.
 267:      */
 268:     protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
 269:                                RectangleConstraint constraint) {
 270:         Size2D size1 = arrangeFN(container, g2, constraint.getWidth());
 271:         if (constraint.getHeightRange().contains(size1.getHeight())) {
 272:             return size1;   
 273:         }
 274:         else {
 275:             double h = constraint.getHeightRange().constrain(size1.getHeight());
 276:             RectangleConstraint c2 = constraint.toFixedHeight(h);
 277:             return arrange(container, g2, c2);   
 278:         }
 279:     }
 280:     
 281:     /** 
 282:      * Arranges the container width a fixed width and no constraint on the 
 283:      * height.
 284:      * 
 285:      * @param container  the container.
 286:      * @param g2  the graphics device.
 287:      * @param width  the fixed width.
 288:      * 
 289:      * @return The container size after arranging the contents.
 290:      */
 291:     protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
 292:                                double width) {
 293:         double[] w = new double[5];
 294:         double[] h = new double[5];
 295:         RectangleConstraint c1 = new RectangleConstraint(
 296:             width, null, LengthConstraintType.FIXED,
 297:             0.0, null, LengthConstraintType.NONE
 298:         );
 299:         if (this.topBlock != null) {
 300:             Size2D size = this.topBlock.arrange(g2, c1);
 301:             w[0] = size.width;
 302:             h[0] = size.height;
 303:         }
 304:         if (this.bottomBlock != null) {
 305:             Size2D size = this.bottomBlock.arrange(g2, c1);
 306:             w[1] = size.width;
 307:             h[1] = size.height;
 308:         }
 309:         RectangleConstraint c2 = new RectangleConstraint(
 310:             0.0, new Range(0.0, width), LengthConstraintType.RANGE,
 311:             0.0, null, LengthConstraintType.NONE
 312:         );
 313:         if (this.leftBlock != null) {
 314:             Size2D size = this.leftBlock.arrange(g2, c2);
 315:             w[2] = size.width;
 316:             h[2] = size.height;
 317:         }
 318:         if (this.rightBlock != null) {
 319:             double maxW = Math.max(width - w[2], 0.0);
 320:             RectangleConstraint c3 = new RectangleConstraint(
 321:                 0.0, new Range(Math.min(w[2], maxW), maxW), 
 322:                 LengthConstraintType.RANGE,
 323:                 0.0, null, LengthConstraintType.NONE
 324:             );    
 325:             Size2D size = this.rightBlock.arrange(g2, c3);
 326:             w[3] = size.width;
 327:             h[3] = size.height;
 328:         }
 329:         
 330:         h[2] = Math.max(h[2], h[3]);
 331:         h[3] = h[2];
 332:         
 333:         if (this.centerBlock != null) {
 334:             RectangleConstraint c4 = new RectangleConstraint(
 335:                 width - w[2] - w[3], null, LengthConstraintType.FIXED,
 336:                 0.0, null, LengthConstraintType.NONE
 337:             );    
 338:             Size2D size = this.centerBlock.arrange(g2, c4);
 339:             w[4] = size.width;
 340:             h[4] = size.height;
 341:         }
 342:         double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
 343:         return arrange(container, g2, new RectangleConstraint(width, height));
 344:     }
 345: 
 346:     /**
 347:      * Performs an arrangement with range constraints on both the vertical 
 348:      * and horizontal sides.
 349:      * 
 350:      * @param container  the container.
 351:      * @param widthRange  the allowable range for the container width.
 352:      * @param heightRange  the allowable range for the container height.
 353:      * @param g2  the graphics device.
 354:      * 
 355:      * @return The container size.
 356:      */
 357:     protected Size2D arrangeRR(BlockContainer container, 
 358:                                Range widthRange, Range heightRange, 
 359:                                Graphics2D g2) {
 360:         double[] w = new double[5];
 361:         double[] h = new double[5];
 362:         if (this.topBlock != null) {
 363:             RectangleConstraint c1 = new RectangleConstraint(
 364:                 widthRange, heightRange
 365:             );
 366:             Size2D size = this.topBlock.arrange(g2, c1);
 367:             w[0] = size.width;
 368:             h[0] = size.height;
 369:         }
 370:         if (this.bottomBlock != null) {
 371:             Range heightRange2 = Range.shift(heightRange, -h[0], false);
 372:             RectangleConstraint c2 = new RectangleConstraint(
 373:                 widthRange, heightRange2
 374:             );  
 375:             Size2D size = this.bottomBlock.arrange(g2, c2);
 376:             w[1] = size.width;
 377:             h[1] = size.height;
 378:         }
 379:         Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1]));
 380:         if (this.leftBlock != null) {
 381:             RectangleConstraint c3 = new RectangleConstraint(
 382:                 widthRange, heightRange3
 383:             );
 384:             Size2D size = this.leftBlock.arrange(g2, c3);
 385:             w[2] = size.width;
 386:             h[2] = size.height;
 387:         }
 388:         Range widthRange2 = Range.shift(widthRange, -w[2], false);
 389:         if (this.rightBlock != null) {
 390:             RectangleConstraint c4 = new RectangleConstraint(
 391:                 widthRange2, heightRange3
 392:             );
 393:             Size2D size = this.rightBlock.arrange(g2, c4);
 394:             w[3] = size.width;
 395:             h[3] = size.height;
 396:         }
 397:         
 398:         h[2] = Math.max(h[2], h[3]);
 399:         h[3] = h[2];
 400:         Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]), false);
 401:         if (this.centerBlock != null) {
 402:             RectangleConstraint c5 = new RectangleConstraint(
 403:                 widthRange3, heightRange3
 404:             );
 405:             // TODO:  the width and height ranges should be reduced by the 
 406:             // height required for the top and bottom, and the width required
 407:             // by the left and right 
 408:             Size2D size = this.centerBlock.arrange(g2, c5);
 409:             w[4] = size.width;
 410:             h[4] = size.height;
 411:         }
 412:         double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
 413:         double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
 414:         if (this.topBlock != null) {
 415:             this.topBlock.setBounds(
 416:                 new Rectangle2D.Double(0.0, 0.0, width, h[0])
 417:             );
 418:         }
 419:         if (this.bottomBlock != null) {
 420:             this.bottomBlock.setBounds(
 421:                 new Rectangle2D.Double(0.0, height - h[1], width, h[1])
 422:             );
 423:         }
 424:         if (this.leftBlock != null) {
 425:             this.leftBlock.setBounds(
 426:                 new Rectangle2D.Double(0.0, h[0], w[2], h[2])
 427:             );
 428:         }
 429:         if (this.rightBlock != null) {
 430:             this.rightBlock.setBounds(
 431:                 new Rectangle2D.Double(width - w[3], h[0], w[3], h[3])
 432:             );
 433:         }
 434:         
 435:         if (this.centerBlock != null) {
 436:             this.centerBlock.setBounds(
 437:                 new Rectangle2D.Double(
 438:                     w[2], h[0], width - w[2] - w[3], height - h[0] - h[1]
 439:                 )
 440:             );
 441:         }
 442:         return new Size2D(width, height);
 443:     }
 444: 
 445:     /**
 446:      * Arranges the items within a container.
 447:      * 
 448:      * @param container  the container.
 449:      * @param constraint  the constraint.
 450:      * @param g2  the graphics device.
 451:      * 
 452:      * @return The container size after the arrangement.
 453:      */
 454:     protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
 455:                                RectangleConstraint constraint) {
 456:         double[] w = new double[5];
 457:         double[] h = new double[5];
 458:         w[0] = constraint.getWidth();
 459:         if (this.topBlock != null) {
 460:             RectangleConstraint c1 = new RectangleConstraint(
 461:                 w[0], null, LengthConstraintType.FIXED,
 462:                 0.0, new Range(0.0, constraint.getHeight()), 
 463:                 LengthConstraintType.RANGE
 464:             );
 465:             Size2D size = this.topBlock.arrange(g2, c1);
 466:             h[0] = size.height;
 467:         }
 468:         w[1] = w[0];
 469:         if (this.bottomBlock != null) {
 470:             RectangleConstraint c2 = new RectangleConstraint(
 471:                 w[0], null, LengthConstraintType.FIXED,
 472:                 0.0, new Range(0.0, constraint.getHeight() - h[0]), 
 473:                 LengthConstraintType.RANGE
 474:             );
 475:             Size2D size = this.bottomBlock.arrange(g2, c2);
 476:             h[1] = size.height;
 477:         }
 478:         h[2] = constraint.getHeight() - h[1] - h[0];
 479:         if (this.leftBlock != null) {
 480:             RectangleConstraint c3 = new RectangleConstraint(
 481:                 0.0, new Range(0.0, constraint.getWidth()), 
 482:                 LengthConstraintType.RANGE,
 483:                 h[2], null, LengthConstraintType.FIXED
 484:             );
 485:             Size2D size = this.leftBlock.arrange(g2, c3);
 486:             w[2] = size.width;            
 487:         }
 488:         h[3] = h[2];
 489:         if (this.rightBlock != null) {
 490:             RectangleConstraint c4 = new RectangleConstraint(
 491:                 0.0, new Range(0.0, constraint.getWidth() - w[2]), 
 492:                 LengthConstraintType.RANGE,
 493:                 h[2], null, LengthConstraintType.FIXED
 494:             );
 495:             Size2D size = this.rightBlock.arrange(g2, c4);
 496:             w[3] = size.width;            
 497:         }
 498:         h[4] = h[2];
 499:         w[4] = constraint.getWidth() - w[3] - w[2];
 500:         RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]);
 501:         if (this.centerBlock != null) {
 502:             this.centerBlock.arrange(g2, c5);   
 503:         }
 504:        
 505:         if (this.topBlock != null) {
 506:             this.topBlock.setBounds(
 507:                 new Rectangle2D.Double(0.0, 0.0, w[0], h[0])
 508:             );
 509:         }
 510:         if (this.bottomBlock != null) {
 511:             this.bottomBlock.setBounds(
 512:                 new Rectangle2D.Double(0.0, h[0] + h[2], w[1], h[1])
 513:             );
 514:         }
 515:         if (this.leftBlock != null) {
 516:             this.leftBlock.setBounds(
 517:                 new Rectangle2D.Double(0.0, h[0], w[2], h[2])
 518:             );
 519:         }
 520:         if (this.rightBlock != null) {
 521:             this.rightBlock.setBounds(
 522:                 new Rectangle2D.Double(w[2] + w[4], h[0], w[3], h[3])
 523:             );
 524:         }
 525:         if (this.centerBlock != null) {
 526:             this.centerBlock.setBounds(
 527:                 new Rectangle2D.Double(w[2], h[0], w[4], h[4])
 528:             );
 529:         }
 530:         return new Size2D(constraint.getWidth(), constraint.getHeight());
 531:     }
 532:     
 533:     /**
 534:      * Clears the layout.
 535:      */
 536:     public void clear() {
 537:         this.centerBlock = null;
 538:         this.topBlock = null;
 539:         this.bottomBlock = null;
 540:         this.leftBlock = null;
 541:         this.rightBlock = null;
 542:     }
 543:     
 544:     /**
 545:      * Tests this arrangement for equality with an arbitrary object.
 546:      * 
 547:      * @param obj  the object (<code>null</code> permitted).
 548:      * 
 549:      * @return A boolean.
 550:      */
 551:     public boolean equals(Object obj) {
 552:         if (obj == this) {
 553:             return true;   
 554:         }
 555:         if (!(obj instanceof BorderArrangement)) {
 556:             return false;   
 557:         }
 558:         BorderArrangement that = (BorderArrangement) obj;
 559:         if (!ObjectUtilities.equal(this.topBlock, that.topBlock)) {
 560:             return false;   
 561:         }
 562:         if (!ObjectUtilities.equal(this.bottomBlock, that.bottomBlock)) {
 563:             return false;   
 564:         }
 565:         if (!ObjectUtilities.equal(this.leftBlock, that.leftBlock)) {
 566:             return false;   
 567:         }
 568:         if (!ObjectUtilities.equal(this.rightBlock, that.rightBlock)) {
 569:             return false;   
 570:         }
 571:         if (!ObjectUtilities.equal(this.centerBlock, that.centerBlock)) {
 572:             return false;   
 573:         }
 574:         return true;
 575:     }
 576: }