Source for org.jfree.chart.block.ColumnArrangement

   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:  * ColumnArrangement.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:  * 04-Feb-2005 : Added equals() and implemented Serializable (DG);
  39:  * 
  40:  */
  41: 
  42: package org.jfree.chart.block;
  43: 
  44: import java.awt.Graphics2D;
  45: import java.awt.geom.Rectangle2D;
  46: import java.io.Serializable;
  47: import java.util.ArrayList;
  48: import java.util.List;
  49: 
  50: import org.jfree.ui.HorizontalAlignment;
  51: import org.jfree.ui.Size2D;
  52: import org.jfree.ui.VerticalAlignment;
  53: 
  54: /**
  55:  * Arranges blocks in a column layout.  This class is immutable.
  56:  */
  57: public class ColumnArrangement implements Arrangement, Serializable {
  58: 
  59:     /** For serialization. */
  60:     private static final long serialVersionUID = -5315388482898581555L;
  61:     
  62:     /** The horizontal alignment of blocks. */
  63:     private HorizontalAlignment horizontalAlignment;
  64:     
  65:     /** The vertical alignment of blocks within each row. */
  66:     private VerticalAlignment verticalAlignment;
  67:     
  68:     /** The horizontal gap between columns. */
  69:     private double horizontalGap;
  70:     
  71:     /** The vertical gap between items in a column. */
  72:     private double verticalGap;
  73:     
  74:     /**
  75:      * Creates a new instance.
  76:      */
  77:     public ColumnArrangement() {   
  78:     }
  79:     
  80:     /**
  81:      * Creates a new instance.
  82:      * 
  83:      * @param hAlign  the horizontal alignment (currently ignored).
  84:      * @param vAlign  the vertical alignment (currently ignored).
  85:      * @param hGap  the horizontal gap.
  86:      * @param vGap  the vertical gap.
  87:      */
  88:     public ColumnArrangement(HorizontalAlignment hAlign, 
  89:                              VerticalAlignment vAlign,
  90:                              double hGap, double vGap) {        
  91:         this.horizontalAlignment = hAlign;
  92:         this.verticalAlignment = vAlign;
  93:         this.horizontalGap = hGap;
  94:         this.verticalGap = vGap;
  95:     }
  96:     
  97:     /**
  98:      * Adds a block to be managed by this instance.  This method is usually 
  99:      * called by the {@link BlockContainer}, you shouldn't need to call it 
 100:      * directly.
 101:      * 
 102:      * @param block  the block.
 103:      * @param key  a key that controls the position of the block.
 104:      */
 105:     public void add(Block block, Object key) {
 106:         // since the flow layout is relatively straightforward, no information
 107:         // needs to be recorded here
 108:     }
 109:     
 110:     /**
 111:      * Calculates and sets the bounds of all the items in the specified 
 112:      * container, subject to the given constraint.  The <code>Graphics2D</code>
 113:      * can be used by some items (particularly items containing text) to 
 114:      * calculate sizing parameters.
 115:      * 
 116:      * @param container  the container whose items are being arranged.
 117:      * @param g2  the graphics device.
 118:      * @param constraint  the size constraint.
 119:      * 
 120:      * @return The size of the container after arrangement of the contents.
 121:      */
 122:     public Size2D arrange(BlockContainer container, Graphics2D g2,
 123:                           RectangleConstraint constraint) {
 124:         
 125:         LengthConstraintType w = constraint.getWidthConstraintType();
 126:         LengthConstraintType h = constraint.getHeightConstraintType();
 127:         if (w == LengthConstraintType.NONE) {
 128:             if (h == LengthConstraintType.NONE) {
 129:                 return arrangeNN(container, g2);  
 130:             }
 131:             else if (h == LengthConstraintType.FIXED) {
 132:                 throw new RuntimeException("Not implemented.");  
 133:             }
 134:             else if (h == LengthConstraintType.RANGE) {
 135:                 throw new RuntimeException("Not implemented.");  
 136:             }
 137:         }
 138:         else if (w == LengthConstraintType.FIXED) {
 139:             if (h == LengthConstraintType.NONE) {
 140:                 throw new RuntimeException("Not implemented.");  
 141:             }
 142:             else if (h == LengthConstraintType.FIXED) {
 143:                 return arrangeFF(container, g2, constraint); 
 144:             }
 145:             else if (h == LengthConstraintType.RANGE) {
 146:                 throw new RuntimeException("Not implemented.");  
 147:             }
 148:         }
 149:         else if (w == LengthConstraintType.RANGE) {
 150:             if (h == LengthConstraintType.NONE) {
 151:                 throw new RuntimeException("Not implemented.");  
 152:             }
 153:             else if (h == LengthConstraintType.FIXED) {
 154:                 return arrangeRF(container, g2, constraint);  
 155:             }
 156:             else if (h == LengthConstraintType.RANGE) {
 157:                 return arrangeRR(container, g2, constraint);  
 158:             }
 159:         }
 160:         return new Size2D();  // TODO: complete this
 161:         
 162:     }
 163: 
 164:     /**
 165:      * Calculates and sets the bounds of all the items in the specified 
 166:      * container, subject to the given constraint.  The <code>Graphics2D</code>
 167:      * can be used by some items (particularly items containing text) to 
 168:      * calculate sizing parameters.
 169:      * 
 170:      * @param container  the container whose items are being arranged.
 171:      * @param g2  the graphics device.
 172:      * @param constraint  the size constraint.
 173:      * 
 174:      * @return The container size after the arrangement.
 175:      */
 176:     protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
 177:                                RectangleConstraint constraint) {
 178:         // TODO: implement properly
 179:         return arrangeNF(container, g2, constraint);
 180:     }
 181:     
 182:     /**
 183:      * Calculates and sets the bounds of all the items in the specified 
 184:      * container, subject to the given constraint.  The <code>Graphics2D</code>
 185:      * can be used by some items (particularly items containing text) to 
 186:      * calculate sizing parameters.
 187:      * 
 188:      * @param container  the container whose items are being arranged.
 189:      * @param constraint  the size constraint.
 190:      * @param g2  the graphics device.
 191:      * 
 192:      * @return The container size after the arrangement.
 193:      */
 194:     protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
 195:                                RectangleConstraint constraint) {
 196:     
 197:         List blocks = container.getBlocks();
 198:         
 199:         double height = constraint.getHeight();
 200:         if (height <= 0.0) {
 201:             height = Double.POSITIVE_INFINITY;
 202:         }
 203:         
 204:         double x = 0.0;
 205:         double y = 0.0;
 206:         double maxWidth = 0.0;
 207:         List itemsInColumn = new ArrayList();
 208:         for (int i = 0; i < blocks.size(); i++) {
 209:             Block block = (Block) blocks.get(i);
 210:             Size2D size = block.arrange(g2, RectangleConstraint.NONE);
 211:             if (y + size.height <= height) {
 212:                 itemsInColumn.add(block);
 213:                 block.setBounds(
 214:                     new Rectangle2D.Double(x, y, size.width, size.height)
 215:                 );
 216:                 y = y + size.height + this.verticalGap;
 217:                 maxWidth = Math.max(maxWidth, size.width);
 218:             }
 219:             else {
 220:                 if (itemsInColumn.isEmpty()) {
 221:                     // place in this column (truncated) anyway
 222:                     block.setBounds(
 223:                         new Rectangle2D.Double(
 224:                             x, y, size.width, Math.min(size.height, height - y)
 225:                         )
 226:                     );
 227:                     y = 0.0;
 228:                     x = x + size.width + this.horizontalGap;
 229:                 }
 230:                 else {
 231:                     // start new column
 232:                     itemsInColumn.clear();
 233:                     x = x + maxWidth + this.horizontalGap;
 234:                     y = 0.0;
 235:                     maxWidth = size.width;
 236:                     block.setBounds(
 237:                         new Rectangle2D.Double(
 238:                             x, y, size.width, Math.min(size.height, height)
 239:                         )
 240:                     );
 241:                     y = size.height + this.verticalGap;
 242:                     itemsInColumn.add(block);
 243:                 }
 244:             }
 245:         }
 246:         return new Size2D(x + maxWidth, constraint.getHeight());  
 247:     }
 248: 
 249:     protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
 250:                                RectangleConstraint constraint) {
 251: 
 252:         // first arrange without constraints, and see if this fits within
 253:         // the required ranges...
 254:         Size2D s1 = arrangeNN(container, g2);
 255:         if (constraint.getHeightRange().contains(s1.height)) {
 256:             return s1;  // TODO: we didn't check the width yet
 257:         }
 258:         else {
 259:             RectangleConstraint c = constraint.toFixedHeight(
 260:                 constraint.getHeightRange().getUpperBound()
 261:             );
 262:             return arrangeRF(container, g2, c);
 263:         }
 264:     }
 265:     
 266:     /**
 267:      * Arranges the blocks in the container using a fixed height and a
 268:      * range for the width.
 269:      * 
 270:      * @param container  the container.
 271:      * @param g2  the graphics device.
 272:      * @param constraint  the constraint.
 273:      * 
 274:      * @return The size of the container after arrangement.
 275:      */
 276:     protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
 277:                                RectangleConstraint constraint) {
 278: 
 279:         Size2D s = arrangeNF(container, g2, constraint);
 280:         if (constraint.getWidthRange().contains(s.width)) {
 281:             return s;   
 282:         }
 283:         else {
 284:             RectangleConstraint c = constraint.toFixedWidth(
 285:                 constraint.getWidthRange().constrain(s.getWidth())
 286:             );
 287:             return arrangeFF(container, g2, c);
 288:         }
 289:     }
 290: 
 291:     /**
 292:      * Arranges the blocks without any constraints.  This puts all blocks
 293:      * into a single column.
 294:      * 
 295:      * @param container  the container.
 296:      * @param g2  the graphics device.
 297:      * 
 298:      * @return The size after the arrangement.
 299:      */
 300:     protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
 301:         double y = 0.0;
 302:         double height = 0.0;
 303:         double maxWidth = 0.0;
 304:         List blocks = container.getBlocks();
 305:         int blockCount = blocks.size();
 306:         if (blockCount > 0) {
 307:             Size2D[] sizes = new Size2D[blocks.size()];
 308:             for (int i = 0; i < blocks.size(); i++) {
 309:                 Block block = (Block) blocks.get(i);
 310:                 sizes[i] = block.arrange(g2, RectangleConstraint.NONE);
 311:                 height = height + sizes[i].getHeight();
 312:                 maxWidth = Math.max(sizes[i].width, maxWidth);
 313:                 block.setBounds(
 314:                     new Rectangle2D.Double(
 315:                         0.0, y, sizes[i].width, sizes[i].height
 316:                     )
 317:                 );
 318:                 y = y + sizes[i].height + this.verticalGap;
 319:             }
 320:             if (blockCount > 1) {
 321:                 height = height + this.verticalGap * (blockCount - 1);   
 322:             }
 323:             if (this.horizontalAlignment != HorizontalAlignment.LEFT) {
 324:                 for (int i = 0; i < blocks.size(); i++) {
 325:                     //Block b = (Block) blocks.get(i);
 326:                     if (this.horizontalAlignment 
 327:                             == HorizontalAlignment.CENTER) {
 328:                         //TODO: shift block right by half
 329:                     }
 330:                     else if (this.horizontalAlignment 
 331:                             == HorizontalAlignment.RIGHT) {
 332:                         //TODO: shift block over to right
 333:                     }
 334:                 }            
 335:             }
 336:         }
 337:         return new Size2D(maxWidth, height);
 338:     }
 339: 
 340:     /**
 341:      * Clears any cached information.
 342:      */
 343:     public void clear() {
 344:         // no action required.
 345:     }
 346:     
 347:     /**
 348:      * Tests this instance for equality with an arbitrary object.
 349:      * 
 350:      * @param obj  the object (<code>null</code> permitted).
 351:      * 
 352:      * @return A boolean.
 353:      */
 354:     public boolean equals(Object obj) {
 355:         if (obj == this) {
 356:             return true;   
 357:         }
 358:         if (!(obj instanceof ColumnArrangement)) {
 359:             return false;   
 360:         }
 361:         ColumnArrangement that = (ColumnArrangement) obj;
 362:         if (this.horizontalAlignment != that.horizontalAlignment) {
 363:             return false;
 364:         }
 365:         if (this.verticalAlignment != that.verticalAlignment) {
 366:             return false;
 367:         }
 368:         if (this.horizontalGap != that.horizontalGap) {
 369:             return false;   
 370:         }
 371:         if (this.verticalGap != that.verticalGap) {
 372:             return false;   
 373:         }
 374:         return true;
 375:     }
 376:     
 377: 
 378: }