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 }