001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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-2007, 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 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 }