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