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 }