001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 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 042package org.jfree.chart.block; 043 044import java.awt.Graphics2D; 045import java.awt.geom.Rectangle2D; 046import java.io.Serializable; 047import java.util.ArrayList; 048import java.util.List; 049 050import org.jfree.ui.HorizontalAlignment; 051import org.jfree.ui.Size2D; 052import org.jfree.ui.VerticalAlignment; 053 054/** 055 * Arranges blocks in a column layout. This class is immutable. 056 */ 057public 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 @Override 106 public void add(Block block, Object key) { 107 // since the flow layout is relatively straightforward, no information 108 // needs to be recorded here 109 } 110 111 /** 112 * Calculates and sets the bounds of all the items in the specified 113 * container, subject to the given constraint. The <code>Graphics2D</code> 114 * can be used by some items (particularly items containing text) to 115 * calculate sizing parameters. 116 * 117 * @param container the container whose items are being arranged. 118 * @param g2 the graphics device. 119 * @param constraint the size constraint. 120 * 121 * @return The size of the container after arrangement of the contents. 122 */ 123 @Override 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 /** 252 * Arranges a container with range constraints for both the horizontal 253 * and vertical. 254 * 255 * @param container the container. 256 * @param g2 the graphics device. 257 * @param constraint the constraint. 258 * 259 * @return The size of the container. 260 */ 261 protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, 262 RectangleConstraint constraint) { 263 264 // first arrange without constraints, and see if this fits within 265 // the required ranges... 266 Size2D s1 = arrangeNN(container, g2); 267 if (constraint.getHeightRange().contains(s1.height)) { 268 return s1; // TODO: we didn't check the width yet 269 } 270 else { 271 RectangleConstraint c = constraint.toFixedHeight( 272 constraint.getHeightRange().getUpperBound() 273 ); 274 return arrangeRF(container, g2, c); 275 } 276 } 277 278 /** 279 * Arranges the blocks in the container using a fixed height and a 280 * range for the width. 281 * 282 * @param container the container. 283 * @param g2 the graphics device. 284 * @param constraint the constraint. 285 * 286 * @return The size of the container after arrangement. 287 */ 288 protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, 289 RectangleConstraint constraint) { 290 291 Size2D s = arrangeNF(container, g2, constraint); 292 if (constraint.getWidthRange().contains(s.width)) { 293 return s; 294 } 295 else { 296 RectangleConstraint c = constraint.toFixedWidth( 297 constraint.getWidthRange().constrain(s.getWidth()) 298 ); 299 return arrangeFF(container, g2, c); 300 } 301 } 302 303 /** 304 * Arranges the blocks without any constraints. This puts all blocks 305 * into a single column. 306 * 307 * @param container the container. 308 * @param g2 the graphics device. 309 * 310 * @return The size after the arrangement. 311 */ 312 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { 313 double y = 0.0; 314 double height = 0.0; 315 double maxWidth = 0.0; 316 List blocks = container.getBlocks(); 317 int blockCount = blocks.size(); 318 if (blockCount > 0) { 319 Size2D[] sizes = new Size2D[blocks.size()]; 320 for (int i = 0; i < blocks.size(); i++) { 321 Block block = (Block) blocks.get(i); 322 sizes[i] = block.arrange(g2, RectangleConstraint.NONE); 323 height = height + sizes[i].getHeight(); 324 maxWidth = Math.max(sizes[i].width, maxWidth); 325 block.setBounds( 326 new Rectangle2D.Double( 327 0.0, y, sizes[i].width, sizes[i].height 328 ) 329 ); 330 y = y + sizes[i].height + this.verticalGap; 331 } 332 if (blockCount > 1) { 333 height = height + this.verticalGap * (blockCount - 1); 334 } 335 if (this.horizontalAlignment != HorizontalAlignment.LEFT) { 336 for (int i = 0; i < blocks.size(); i++) { 337 //Block b = (Block) blocks.get(i); 338 if (this.horizontalAlignment 339 == HorizontalAlignment.CENTER) { 340 //TODO: shift block right by half 341 } 342 else if (this.horizontalAlignment 343 == HorizontalAlignment.RIGHT) { 344 //TODO: shift block over to right 345 } 346 } 347 } 348 } 349 return new Size2D(maxWidth, height); 350 } 351 352 /** 353 * Clears any cached information. 354 */ 355 @Override 356 public void clear() { 357 // no action required. 358 } 359 360 /** 361 * Tests this instance for equality with an arbitrary object. 362 * 363 * @param obj the object (<code>null</code> permitted). 364 * 365 * @return A boolean. 366 */ 367 @Override 368 public boolean equals(Object obj) { 369 if (obj == this) { 370 return true; 371 } 372 if (!(obj instanceof ColumnArrangement)) { 373 return false; 374 } 375 ColumnArrangement that = (ColumnArrangement) obj; 376 if (this.horizontalAlignment != that.horizontalAlignment) { 377 return false; 378 } 379 if (this.verticalAlignment != that.verticalAlignment) { 380 return false; 381 } 382 if (this.horizontalGap != that.horizontalGap) { 383 return false; 384 } 385 if (this.verticalGap != that.verticalGap) { 386 return false; 387 } 388 return true; 389 } 390 391 392}