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 }