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-2005, 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, 2005, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: BorderArrangement.java,v 1.14.2.1 2005/10/25 20:39:38 mungady Exp $
  36:  *
  37:  * Changes:
  38:  * --------
  39:  * 22-Oct-2004 : Version 1 (DG);
  40:  * 08-Feb-2005 : Updated for changes in RectangleConstraint (DG);
  41:  * 24-Feb-2005 : Improved arrangeRR() method (DG);
  42:  * 03-May-2005 : Implemented Serializable and added equals() method (DG);
  43:  * 13-May-2005 : Fixed bugs in the arrange() method (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(
 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: }