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(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: }