001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------------------- 028 * ColumnArrangement.java 029 * ---------------------- 030 * (C) Copyright 2004-2008, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes: 036 * -------- 037 * 22-Oct-2004 : Version 1 (DG); 038 * 04-Feb-2005 : Added equals() and implemented Serializable (DG); 039 * 040 */ 041 042 package org.jfree.chart.block; 043 044 import java.awt.Graphics2D; 045 import java.awt.geom.Rectangle2D; 046 import java.io.Serializable; 047 import java.util.ArrayList; 048 import java.util.List; 049 050 import org.jfree.ui.HorizontalAlignment; 051 import org.jfree.ui.Size2D; 052 import org.jfree.ui.VerticalAlignment; 053 054 /** 055 * Arranges blocks in a column layout. This class is immutable. 056 */ 057 public class ColumnArrangement implements Arrangement, Serializable { 058 059 /** For serialization. */ 060 private static final long serialVersionUID = -5315388482898581555L; 061 062 /** The horizontal alignment of blocks. */ 063 private HorizontalAlignment horizontalAlignment; 064 065 /** The vertical alignment of blocks within each row. */ 066 private VerticalAlignment verticalAlignment; 067 068 /** The horizontal gap between columns. */ 069 private double horizontalGap; 070 071 /** The vertical gap between items in a column. */ 072 private double verticalGap; 073 074 /** 075 * Creates a new instance. 076 */ 077 public ColumnArrangement() { 078 } 079 080 /** 081 * Creates a new instance. 082 * 083 * @param hAlign the horizontal alignment (currently ignored). 084 * @param vAlign the vertical alignment (currently ignored). 085 * @param hGap the horizontal gap. 086 * @param vGap the vertical gap. 087 */ 088 public ColumnArrangement(HorizontalAlignment hAlign, 089 VerticalAlignment vAlign, 090 double hGap, double vGap) { 091 this.horizontalAlignment = hAlign; 092 this.verticalAlignment = vAlign; 093 this.horizontalGap = hGap; 094 this.verticalGap = vGap; 095 } 096 097 /** 098 * Adds a block to be managed by this instance. This method is usually 099 * 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 /** 250 * Arranges a container with range constraints for both the horizontal 251 * and vertical. 252 * 253 * @param container the container. 254 * @param g2 the graphics device. 255 * @param constraint the constraint. 256 * 257 * @return The size of the container. 258 */ 259 protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, 260 RectangleConstraint constraint) { 261 262 // first arrange without constraints, and see if this fits within 263 // the required ranges... 264 Size2D s1 = arrangeNN(container, g2); 265 if (constraint.getHeightRange().contains(s1.height)) { 266 return s1; // TODO: we didn't check the width yet 267 } 268 else { 269 RectangleConstraint c = constraint.toFixedHeight( 270 constraint.getHeightRange().getUpperBound() 271 ); 272 return arrangeRF(container, g2, c); 273 } 274 } 275 276 /** 277 * Arranges the blocks in the container using a fixed height and a 278 * range for the width. 279 * 280 * @param container the container. 281 * @param g2 the graphics device. 282 * @param constraint the constraint. 283 * 284 * @return The size of the container after arrangement. 285 */ 286 protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, 287 RectangleConstraint constraint) { 288 289 Size2D s = arrangeNF(container, g2, constraint); 290 if (constraint.getWidthRange().contains(s.width)) { 291 return s; 292 } 293 else { 294 RectangleConstraint c = constraint.toFixedWidth( 295 constraint.getWidthRange().constrain(s.getWidth()) 296 ); 297 return arrangeFF(container, g2, c); 298 } 299 } 300 301 /** 302 * Arranges the blocks without any constraints. This puts all blocks 303 * into a single column. 304 * 305 * @param container the container. 306 * @param g2 the graphics device. 307 * 308 * @return The size after the arrangement. 309 */ 310 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { 311 double y = 0.0; 312 double height = 0.0; 313 double maxWidth = 0.0; 314 List blocks = container.getBlocks(); 315 int blockCount = blocks.size(); 316 if (blockCount > 0) { 317 Size2D[] sizes = new Size2D[blocks.size()]; 318 for (int i = 0; i < blocks.size(); i++) { 319 Block block = (Block) blocks.get(i); 320 sizes[i] = block.arrange(g2, RectangleConstraint.NONE); 321 height = height + sizes[i].getHeight(); 322 maxWidth = Math.max(sizes[i].width, maxWidth); 323 block.setBounds( 324 new Rectangle2D.Double( 325 0.0, y, sizes[i].width, sizes[i].height 326 ) 327 ); 328 y = y + sizes[i].height + this.verticalGap; 329 } 330 if (blockCount > 1) { 331 height = height + this.verticalGap * (blockCount - 1); 332 } 333 if (this.horizontalAlignment != HorizontalAlignment.LEFT) { 334 for (int i = 0; i < blocks.size(); i++) { 335 //Block b = (Block) blocks.get(i); 336 if (this.horizontalAlignment 337 == HorizontalAlignment.CENTER) { 338 //TODO: shift block right by half 339 } 340 else if (this.horizontalAlignment 341 == HorizontalAlignment.RIGHT) { 342 //TODO: shift block over to right 343 } 344 } 345 } 346 } 347 return new Size2D(maxWidth, height); 348 } 349 350 /** 351 * Clears any cached information. 352 */ 353 public void clear() { 354 // no action required. 355 } 356 357 /** 358 * Tests this instance for equality with an arbitrary object. 359 * 360 * @param obj the object (<code>null</code> permitted). 361 * 362 * @return A boolean. 363 */ 364 public boolean equals(Object obj) { 365 if (obj == this) { 366 return true; 367 } 368 if (!(obj instanceof ColumnArrangement)) { 369 return false; 370 } 371 ColumnArrangement that = (ColumnArrangement) obj; 372 if (this.horizontalAlignment != that.horizontalAlignment) { 373 return false; 374 } 375 if (this.verticalAlignment != that.verticalAlignment) { 376 return false; 377 } 378 if (this.horizontalGap != that.horizontalGap) { 379 return false; 380 } 381 if (this.verticalGap != that.verticalGap) { 382 return false; 383 } 384 return true; 385 } 386 387 388 }