001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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, 2005, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: ColumnArrangement.java,v 1.12.2.2 2006/08/04 11:48:41 mungady Exp $ 036 * 037 * Changes: 038 * -------- 039 * 22-Oct-2004 : Version 1 (DG); 040 * 04-Feb-2005 : Added equals() and implemented Serializable (DG); 041 * 042 */ 043 044 package org.jfree.chart.block; 045 046 import java.awt.Graphics2D; 047 import java.awt.geom.Rectangle2D; 048 import java.io.Serializable; 049 import java.util.ArrayList; 050 import java.util.List; 051 052 import org.jfree.ui.HorizontalAlignment; 053 import org.jfree.ui.Size2D; 054 import org.jfree.ui.VerticalAlignment; 055 056 /** 057 * Arranges blocks in a column layout. This class is immutable. 058 */ 059 public class ColumnArrangement implements Arrangement, Serializable { 060 061 /** For serialization. */ 062 private static final long serialVersionUID = -5315388482898581555L; 063 064 /** The horizontal alignment of blocks. */ 065 private HorizontalAlignment horizontalAlignment; 066 067 /** The vertical alignment of blocks within each row. */ 068 private VerticalAlignment verticalAlignment; 069 070 /** The horizontal gap between columns. */ 071 private double horizontalGap; 072 073 /** The vertical gap between items in a column. */ 074 private double verticalGap; 075 076 /** 077 * Creates a new instance. 078 */ 079 public ColumnArrangement() { 080 } 081 082 /** 083 * Creates a new instance. 084 * 085 * @param hAlign the horizontal alignment (currently ignored). 086 * @param vAlign the vertical alignment (currently ignored). 087 * @param hGap the horizontal gap. 088 * @param vGap the vertical gap. 089 */ 090 public ColumnArrangement(HorizontalAlignment hAlign, 091 VerticalAlignment vAlign, 092 double hGap, double vGap) { 093 this.horizontalAlignment = hAlign; 094 this.verticalAlignment = vAlign; 095 this.horizontalGap = hGap; 096 this.verticalGap = vGap; 097 } 098 099 /** 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 }