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, double hGap, double vGap) {
092: this .horizontalAlignment = hAlign;
093: this .verticalAlignment = vAlign;
094: this .horizontalGap = hGap;
095: this .verticalGap = vGap;
096: }
097:
098: /**
099: * Adds a block to be managed by this instance. This method is usually
100: * called by the {@link BlockContainer}, you shouldn't need to call it
101: * directly.
102: *
103: * @param block the block.
104: * @param key a key that controls the position of the block.
105: */
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: public Size2D arrange(BlockContainer container, Graphics2D g2,
124: RectangleConstraint constraint) {
125:
126: LengthConstraintType w = constraint.getWidthConstraintType();
127: LengthConstraintType h = constraint.getHeightConstraintType();
128: if (w == LengthConstraintType.NONE) {
129: if (h == LengthConstraintType.NONE) {
130: return arrangeNN(container, g2);
131: } else if (h == LengthConstraintType.FIXED) {
132: throw new RuntimeException("Not implemented.");
133: } else if (h == LengthConstraintType.RANGE) {
134: throw new RuntimeException("Not implemented.");
135: }
136: } else if (w == LengthConstraintType.FIXED) {
137: if (h == LengthConstraintType.NONE) {
138: throw new RuntimeException("Not implemented.");
139: } else if (h == LengthConstraintType.FIXED) {
140: return arrangeFF(container, g2, constraint);
141: } else if (h == LengthConstraintType.RANGE) {
142: throw new RuntimeException("Not implemented.");
143: }
144: } else if (w == LengthConstraintType.RANGE) {
145: if (h == LengthConstraintType.NONE) {
146: throw new RuntimeException("Not implemented.");
147: } else if (h == LengthConstraintType.FIXED) {
148: return arrangeRF(container, g2, constraint);
149: } else if (h == LengthConstraintType.RANGE) {
150: return arrangeRR(container, g2, constraint);
151: }
152: }
153: return new Size2D(); // TODO: complete this
154:
155: }
156:
157: /**
158: * Calculates and sets the bounds of all the items in the specified
159: * container, subject to the given constraint. The <code>Graphics2D</code>
160: * can be used by some items (particularly items containing text) to
161: * calculate sizing parameters.
162: *
163: * @param container the container whose items are being arranged.
164: * @param g2 the graphics device.
165: * @param constraint the size constraint.
166: *
167: * @return The container size after the arrangement.
168: */
169: protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
170: RectangleConstraint constraint) {
171: // TODO: implement properly
172: return arrangeNF(container, g2, constraint);
173: }
174:
175: /**
176: * Calculates and sets the bounds of all the items in the specified
177: * container, subject to the given constraint. The <code>Graphics2D</code>
178: * can be used by some items (particularly items containing text) to
179: * calculate sizing parameters.
180: *
181: * @param container the container whose items are being arranged.
182: * @param constraint the size constraint.
183: * @param g2 the graphics device.
184: *
185: * @return The container size after the arrangement.
186: */
187: protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
188: RectangleConstraint constraint) {
189:
190: List blocks = container.getBlocks();
191:
192: double height = constraint.getHeight();
193: if (height <= 0.0) {
194: height = Double.POSITIVE_INFINITY;
195: }
196:
197: double x = 0.0;
198: double y = 0.0;
199: double maxWidth = 0.0;
200: List itemsInColumn = new ArrayList();
201: for (int i = 0; i < blocks.size(); i++) {
202: Block block = (Block) blocks.get(i);
203: Size2D size = block.arrange(g2, RectangleConstraint.NONE);
204: if (y + size.height <= height) {
205: itemsInColumn.add(block);
206: block.setBounds(new Rectangle2D.Double(x, y,
207: size.width, size.height));
208: y = y + size.height + this .verticalGap;
209: maxWidth = Math.max(maxWidth, size.width);
210: } else {
211: if (itemsInColumn.isEmpty()) {
212: // place in this column (truncated) anyway
213: block.setBounds(new Rectangle2D.Double(x, y,
214: size.width, Math.min(size.height, height
215: - y)));
216: y = 0.0;
217: x = x + size.width + this .horizontalGap;
218: } else {
219: // start new column
220: itemsInColumn.clear();
221: x = x + maxWidth + this .horizontalGap;
222: y = 0.0;
223: maxWidth = size.width;
224: block.setBounds(new Rectangle2D.Double(x, y,
225: size.width, Math.min(size.height, height)));
226: y = size.height + this .verticalGap;
227: itemsInColumn.add(block);
228: }
229: }
230: }
231: return new Size2D(x + maxWidth, constraint.getHeight());
232: }
233:
234: protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
235: RectangleConstraint constraint) {
236:
237: // first arrange without constraints, and see if this fits within
238: // the required ranges...
239: Size2D s1 = arrangeNN(container, g2);
240: if (constraint.getHeightRange().contains(s1.height)) {
241: return s1; // TODO: we didn't check the width yet
242: } else {
243: RectangleConstraint c = constraint.toFixedHeight(constraint
244: .getHeightRange().getUpperBound());
245: return arrangeRF(container, g2, c);
246: }
247: }
248:
249: /**
250: * Arranges the blocks in the container using a fixed height and a
251: * range for the width.
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 after arrangement.
258: */
259: protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
260: RectangleConstraint constraint) {
261:
262: Size2D s = arrangeNF(container, g2, constraint);
263: if (constraint.getWidthRange().contains(s.width)) {
264: return s;
265: } else {
266: RectangleConstraint c = constraint.toFixedWidth(constraint
267: .getWidthRange().constrain(s.getWidth()));
268: return arrangeFF(container, g2, c);
269: }
270: }
271:
272: /**
273: * Arranges the blocks without any constraints. This puts all blocks
274: * into a single column.
275: *
276: * @param container the container.
277: * @param g2 the graphics device.
278: *
279: * @return The size after the arrangement.
280: */
281: protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
282: double y = 0.0;
283: double height = 0.0;
284: double maxWidth = 0.0;
285: List blocks = container.getBlocks();
286: int blockCount = blocks.size();
287: if (blockCount > 0) {
288: Size2D[] sizes = new Size2D[blocks.size()];
289: for (int i = 0; i < blocks.size(); i++) {
290: Block block = (Block) blocks.get(i);
291: sizes[i] = block.arrange(g2, RectangleConstraint.NONE);
292: height = height + sizes[i].getHeight();
293: maxWidth = Math.max(sizes[i].width, maxWidth);
294: block.setBounds(new Rectangle2D.Double(0.0, y,
295: sizes[i].width, sizes[i].height));
296: y = y + sizes[i].height + this .verticalGap;
297: }
298: if (blockCount > 1) {
299: height = height + this .verticalGap * (blockCount - 1);
300: }
301: if (this .horizontalAlignment != HorizontalAlignment.LEFT) {
302: for (int i = 0; i < blocks.size(); i++) {
303: //Block b = (Block) blocks.get(i);
304: if (this .horizontalAlignment == HorizontalAlignment.CENTER) {
305: //TODO: shift block right by half
306: } else if (this .horizontalAlignment == HorizontalAlignment.RIGHT) {
307: //TODO: shift block over to right
308: }
309: }
310: }
311: }
312: return new Size2D(maxWidth, height);
313: }
314:
315: /**
316: * Clears any cached information.
317: */
318: public void clear() {
319: // no action required.
320: }
321:
322: /**
323: * Tests this instance for equality with an arbitrary object.
324: *
325: * @param obj the object (<code>null</code> permitted).
326: *
327: * @return A boolean.
328: */
329: public boolean equals(Object obj) {
330: if (obj == this ) {
331: return true;
332: }
333: if (!(obj instanceof ColumnArrangement)) {
334: return false;
335: }
336: ColumnArrangement that = (ColumnArrangement) obj;
337: if (this .horizontalAlignment != that.horizontalAlignment) {
338: return false;
339: }
340: if (this .verticalAlignment != that.verticalAlignment) {
341: return false;
342: }
343: if (this .horizontalGap != that.horizontalGap) {
344: return false;
345: }
346: if (this .verticalGap != that.verticalGap) {
347: return false;
348: }
349: return true;
350: }
351:
352: }
|