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: * BorderArrangement.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: BorderArrangement.java,v 1.14.2.1 2005/10/25 20:39:38 mungady Exp $
036: *
037: * Changes:
038: * --------
039: * 22-Oct-2004 : Version 1 (DG);
040: * 08-Feb-2005 : Updated for changes in RectangleConstraint (DG);
041: * 24-Feb-2005 : Improved arrangeRR() method (DG);
042: * 03-May-2005 : Implemented Serializable and added equals() method (DG);
043: * 13-May-2005 : Fixed bugs in the arrange() method (DG);
044: *
045: */
046:
047: package org.jfree.chart.block;
048:
049: import java.awt.Graphics2D;
050: import java.awt.geom.Rectangle2D;
051: import java.io.Serializable;
052:
053: import org.jfree.data.Range;
054: import org.jfree.ui.RectangleEdge;
055: import org.jfree.ui.Size2D;
056: import org.jfree.util.ObjectUtilities;
057:
058: /**
059: * An arrangement manager that lays out blocks in a similar way to
060: * Swing's BorderLayout class.
061: */
062: public class BorderArrangement implements Arrangement, Serializable {
063:
064: /** For serialization. */
065: private static final long serialVersionUID = 506071142274883745L;
066:
067: /** The block (if any) at the center of the layout. */
068: private Block centerBlock;
069:
070: /** The block (if any) at the top of the layout. */
071: private Block topBlock;
072:
073: /** The block (if any) at the bottom of the layout. */
074: private Block bottomBlock;
075:
076: /** The block (if any) at the left of the layout. */
077: private Block leftBlock;
078:
079: /** The block (if any) at the right of the layout. */
080: private Block rightBlock;
081:
082: /**
083: * Creates a new instance.
084: */
085: public BorderArrangement() {
086: }
087:
088: /**
089: * Adds a block to the arrangement manager at the specified edge.
090: *
091: * @param block the block (<code>null</code> permitted).
092: * @param key the edge (an instance of {@link RectangleEdge}) or
093: * <code>null</code> for the center block.
094: */
095: public void add(Block block, Object key) {
096:
097: if (key == null) {
098: this .centerBlock = block;
099: } else {
100: RectangleEdge edge = (RectangleEdge) key;
101: if (edge == RectangleEdge.TOP) {
102: this .topBlock = block;
103: } else if (edge == RectangleEdge.BOTTOM) {
104: this .bottomBlock = block;
105: } else if (edge == RectangleEdge.LEFT) {
106: this .leftBlock = block;
107: } else if (edge == RectangleEdge.RIGHT) {
108: this .rightBlock = block;
109: }
110: }
111: }
112:
113: /**
114: * Arranges the items in the specified container, subject to the given
115: * constraint.
116: *
117: * @param container the container.
118: * @param g2 the graphics device.
119: * @param constraint the constraint.
120: *
121: * @return The block size.
122: */
123: public Size2D arrange(BlockContainer container, Graphics2D g2,
124: RectangleConstraint constraint) {
125: RectangleConstraint contentConstraint = container
126: .toContentConstraint(constraint);
127: Size2D contentSize = null;
128: LengthConstraintType w = contentConstraint
129: .getWidthConstraintType();
130: LengthConstraintType h = contentConstraint
131: .getHeightConstraintType();
132: if (w == LengthConstraintType.NONE) {
133: if (h == LengthConstraintType.NONE) {
134: contentSize = arrangeNN(container, g2);
135: } else if (h == LengthConstraintType.FIXED) {
136: throw new RuntimeException("Not implemented.");
137: } else if (h == LengthConstraintType.RANGE) {
138: throw new RuntimeException("Not implemented.");
139: }
140: } else if (w == LengthConstraintType.FIXED) {
141: if (h == LengthConstraintType.NONE) {
142: contentSize = arrangeFN(container, g2, constraint
143: .getWidth());
144: } else if (h == LengthConstraintType.FIXED) {
145: contentSize = arrangeFF(container, g2, constraint);
146: } else if (h == LengthConstraintType.RANGE) {
147: contentSize = arrangeFR(container, g2, constraint);
148: }
149: } else if (w == LengthConstraintType.RANGE) {
150: if (h == LengthConstraintType.NONE) {
151: throw new RuntimeException("Not implemented.");
152: } else if (h == LengthConstraintType.FIXED) {
153: throw new RuntimeException("Not implemented.");
154: } else if (h == LengthConstraintType.RANGE) {
155: contentSize = arrangeRR(container, constraint
156: .getWidthRange(), constraint.getHeightRange(),
157: g2);
158: }
159: }
160: return new Size2D(container.calculateTotalWidth(contentSize
161: .getWidth()), container
162: .calculateTotalHeight(contentSize.getHeight()));
163: }
164:
165: /**
166: * Performs an arrangement without constraints.
167: *
168: * @param container the container.
169: * @param g2 the graphics device.
170: *
171: * @return The container size after the arrangement.
172: */
173: protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
174: double[] w = new double[5];
175: double[] h = new double[5];
176: if (this .topBlock != null) {
177: Size2D size = this .topBlock.arrange(g2,
178: RectangleConstraint.NONE);
179: w[0] = size.width;
180: h[0] = size.height;
181: }
182: if (this .bottomBlock != null) {
183: Size2D size = this .bottomBlock.arrange(g2,
184: RectangleConstraint.NONE);
185: w[1] = size.width;
186: h[1] = size.height;
187: }
188: if (this .leftBlock != null) {
189: Size2D size = this .leftBlock.arrange(g2,
190: RectangleConstraint.NONE);
191: w[2] = size.width;
192: h[2] = size.height;
193: }
194: if (this .rightBlock != null) {
195: Size2D size = this .rightBlock.arrange(g2,
196: RectangleConstraint.NONE);
197: w[3] = size.width;
198: h[3] = size.height;
199: }
200:
201: h[2] = Math.max(h[2], h[3]);
202: h[3] = h[2];
203:
204: if (this .centerBlock != null) {
205: Size2D size = this .centerBlock.arrange(g2,
206: RectangleConstraint.NONE);
207: w[4] = size.width;
208: h[4] = size.height;
209: }
210: double width = Math.max(w[0], Math
211: .max(w[1], w[2] + w[4] + w[3]));
212: double centerHeight = Math.max(h[2], Math.max(h[3], h[4]));
213: double height = h[0] + h[1] + centerHeight;
214: if (this .topBlock != null) {
215: this .topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0,
216: width, h[0]));
217: }
218: if (this .bottomBlock != null) {
219: this .bottomBlock.setBounds(new Rectangle2D.Double(0.0,
220: height - h[1], width, h[1]));
221: }
222: if (this .leftBlock != null) {
223: this .leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0],
224: w[2], centerHeight));
225: }
226: if (this .rightBlock != null) {
227: this .rightBlock.setBounds(new Rectangle2D.Double(width
228: - w[3], h[0], w[3], centerHeight));
229: }
230:
231: if (this .centerBlock != null) {
232: this .centerBlock.setBounds(new Rectangle2D.Double(w[2],
233: h[0], width - w[2] - w[3], centerHeight));
234: }
235: return new Size2D(width, height);
236: }
237:
238: /**
239: * Performs an arrangement with a fixed width and a range for the height.
240: *
241: * @param container the container.
242: * @param g2 the graphics device.
243: * @param constraint the constraint.
244: *
245: * @return The container size after the arrangement.
246: */
247: protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
248: RectangleConstraint constraint) {
249: Size2D size1 = arrangeFN(container, g2, constraint.getWidth());
250: if (constraint.getHeightRange().contains(size1.getHeight())) {
251: return size1;
252: } else {
253: double h = constraint.getHeightRange().constrain(
254: size1.getHeight());
255: RectangleConstraint c2 = constraint.toFixedHeight(h);
256: return arrange(container, g2, c2);
257: }
258: }
259:
260: /**
261: * Arranges the container width a fixed width and no constraint on the
262: * height.
263: *
264: * @param container the container.
265: * @param g2 the graphics device.
266: * @param width the fixed width.
267: *
268: * @return The container size after arranging the contents.
269: */
270: protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
271: double width) {
272: double[] w = new double[5];
273: double[] h = new double[5];
274: RectangleConstraint c1 = new RectangleConstraint(width, null,
275: LengthConstraintType.FIXED, 0.0, null,
276: LengthConstraintType.NONE);
277: if (this .topBlock != null) {
278: Size2D size = this .topBlock.arrange(g2, c1);
279: w[0] = size.width;
280: h[0] = size.height;
281: }
282: if (this .bottomBlock != null) {
283: Size2D size = this .bottomBlock.arrange(g2, c1);
284: w[1] = size.width;
285: h[1] = size.height;
286: }
287: RectangleConstraint c2 = new RectangleConstraint(0.0,
288: new Range(0.0, width), LengthConstraintType.RANGE, 0.0,
289: null, LengthConstraintType.NONE);
290: if (this .leftBlock != null) {
291: Size2D size = this .leftBlock.arrange(g2, c2);
292: w[2] = size.width;
293: h[2] = size.height;
294: }
295: if (this .rightBlock != null) {
296: double maxW = Math.max(width - w[2], 0.0);
297: RectangleConstraint c3 = new RectangleConstraint(0.0,
298: new Range(Math.min(w[2], maxW), maxW),
299: LengthConstraintType.RANGE, 0.0, null,
300: LengthConstraintType.NONE);
301: Size2D size = this .rightBlock.arrange(g2, c3);
302: w[3] = size.width;
303: h[3] = size.height;
304: }
305:
306: h[2] = Math.max(h[2], h[3]);
307: h[3] = h[2];
308:
309: if (this .centerBlock != null) {
310: RectangleConstraint c4 = new RectangleConstraint(width
311: - w[2] - w[3], null, LengthConstraintType.FIXED,
312: 0.0, null, LengthConstraintType.NONE);
313: Size2D size = this .centerBlock.arrange(g2, c4);
314: w[4] = size.width;
315: h[4] = size.height;
316: }
317: double height = h[0] + h[1]
318: + Math.max(h[2], Math.max(h[3], h[4]));
319: return arrange(container, g2, new RectangleConstraint(width,
320: height));
321: }
322:
323: /**
324: * Performs an arrangement with range constraints on both the vertical
325: * and horizontal sides.
326: *
327: * @param container the container.
328: * @param widthRange the allowable range for the container width.
329: * @param heightRange the allowable range for the container height.
330: * @param g2 the graphics device.
331: *
332: * @return The container size.
333: */
334: protected Size2D arrangeRR(BlockContainer container,
335: Range widthRange, Range heightRange, Graphics2D g2) {
336: double[] w = new double[5];
337: double[] h = new double[5];
338: if (this .topBlock != null) {
339: RectangleConstraint c1 = new RectangleConstraint(
340: widthRange, heightRange);
341: Size2D size = this .topBlock.arrange(g2, c1);
342: w[0] = size.width;
343: h[0] = size.height;
344: }
345: if (this .bottomBlock != null) {
346: Range heightRange2 = Range.shift(heightRange, -h[0], false);
347: RectangleConstraint c2 = new RectangleConstraint(
348: widthRange, heightRange2);
349: Size2D size = this .bottomBlock.arrange(g2, c2);
350: w[1] = size.width;
351: h[1] = size.height;
352: }
353: Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1]));
354: if (this .leftBlock != null) {
355: RectangleConstraint c3 = new RectangleConstraint(
356: widthRange, heightRange3);
357: Size2D size = this .leftBlock.arrange(g2, c3);
358: w[2] = size.width;
359: h[2] = size.height;
360: }
361: Range widthRange2 = Range.shift(widthRange, -w[2], false);
362: if (this .rightBlock != null) {
363: RectangleConstraint c4 = new RectangleConstraint(
364: widthRange2, heightRange3);
365: Size2D size = this .rightBlock.arrange(g2, c4);
366: w[3] = size.width;
367: h[3] = size.height;
368: }
369:
370: h[2] = Math.max(h[2], h[3]);
371: h[3] = h[2];
372: Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]),
373: false);
374: if (this .centerBlock != null) {
375: RectangleConstraint c5 = new RectangleConstraint(
376: widthRange3, heightRange3);
377: // TODO: the width and height ranges should be reduced by the
378: // height required for the top and bottom, and the width required
379: // by the left and right
380: Size2D size = this .centerBlock.arrange(g2, c5);
381: w[4] = size.width;
382: h[4] = size.height;
383: }
384: double width = Math.max(w[0], Math
385: .max(w[1], w[2] + w[4] + w[3]));
386: double height = h[0] + h[1]
387: + Math.max(h[2], Math.max(h[3], h[4]));
388: if (this .topBlock != null) {
389: this .topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0,
390: width, h[0]));
391: }
392: if (this .bottomBlock != null) {
393: this .bottomBlock.setBounds(new Rectangle2D.Double(0.0,
394: height - h[1], width, h[1]));
395: }
396: if (this .leftBlock != null) {
397: this .leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0],
398: w[2], h[2]));
399: }
400: if (this .rightBlock != null) {
401: this .rightBlock.setBounds(new Rectangle2D.Double(width
402: - w[3], h[0], w[3], h[3]));
403: }
404:
405: if (this .centerBlock != null) {
406: this .centerBlock.setBounds(new Rectangle2D.Double(w[2],
407: h[0], width - w[2] - w[3], height - h[0] - h[1]));
408: }
409: return new Size2D(width, height);
410: }
411:
412: /**
413: * Arranges the items within a container.
414: *
415: * @param container the container.
416: * @param constraint the constraint.
417: * @param g2 the graphics device.
418: *
419: * @return The container size after the arrangement.
420: */
421: protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
422: RectangleConstraint constraint) {
423: double[] w = new double[5];
424: double[] h = new double[5];
425: w[0] = constraint.getWidth();
426: if (this .topBlock != null) {
427: RectangleConstraint c1 = new RectangleConstraint(w[0],
428: null, LengthConstraintType.FIXED, 0.0, new Range(
429: 0.0, constraint.getHeight()),
430: LengthConstraintType.RANGE);
431: Size2D size = this .topBlock.arrange(g2, c1);
432: h[0] = size.height;
433: }
434: w[1] = w[0];
435: if (this .bottomBlock != null) {
436: RectangleConstraint c2 = new RectangleConstraint(w[0],
437: null, LengthConstraintType.FIXED, 0.0, new Range(
438: 0.0, constraint.getHeight() - h[0]),
439: LengthConstraintType.RANGE);
440: Size2D size = this .bottomBlock.arrange(g2, c2);
441: h[1] = size.height;
442: }
443: h[2] = constraint.getHeight() - h[1] - h[0];
444: if (this .leftBlock != null) {
445: RectangleConstraint c3 = new RectangleConstraint(0.0,
446: new Range(0.0, constraint.getWidth()),
447: LengthConstraintType.RANGE, h[2], null,
448: LengthConstraintType.FIXED);
449: Size2D size = this .leftBlock.arrange(g2, c3);
450: w[2] = size.width;
451: }
452: h[3] = h[2];
453: if (this .rightBlock != null) {
454: RectangleConstraint c4 = new RectangleConstraint(0.0,
455: new Range(0.0, constraint.getWidth() - w[2]),
456: LengthConstraintType.RANGE, h[2], null,
457: LengthConstraintType.FIXED);
458: Size2D size = this .rightBlock.arrange(g2, c4);
459: w[3] = size.width;
460: }
461: h[4] = h[2];
462: w[4] = constraint.getWidth() - w[3] - w[2];
463: RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]);
464: if (this .centerBlock != null) {
465: this .centerBlock.arrange(g2, c5);
466: }
467:
468: if (this .topBlock != null) {
469: this .topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0,
470: w[0], h[0]));
471: }
472: if (this .bottomBlock != null) {
473: this .bottomBlock.setBounds(new Rectangle2D.Double(0.0, h[0]
474: + h[2], w[1], h[1]));
475: }
476: if (this .leftBlock != null) {
477: this .leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0],
478: w[2], h[2]));
479: }
480: if (this .rightBlock != null) {
481: this .rightBlock.setBounds(new Rectangle2D.Double(w[2]
482: + w[4], h[0], w[3], h[3]));
483: }
484: if (this .centerBlock != null) {
485: this .centerBlock.setBounds(new Rectangle2D.Double(w[2],
486: h[0], w[4], h[4]));
487: }
488: return new Size2D(constraint.getWidth(), constraint.getHeight());
489: }
490:
491: /**
492: * Clears the layout.
493: */
494: public void clear() {
495: this .centerBlock = null;
496: this .topBlock = null;
497: this .bottomBlock = null;
498: this .leftBlock = null;
499: this .rightBlock = null;
500: }
501:
502: /**
503: * Tests this arrangement for equality with an arbitrary object.
504: *
505: * @param obj the object (<code>null</code> permitted).
506: *
507: * @return A boolean.
508: */
509: public boolean equals(Object obj) {
510: if (obj == this ) {
511: return true;
512: }
513: if (!(obj instanceof BorderArrangement)) {
514: return false;
515: }
516: BorderArrangement that = (BorderArrangement) obj;
517: if (!ObjectUtilities.equal(this .topBlock, that.topBlock)) {
518: return false;
519: }
520: if (!ObjectUtilities.equal(this .bottomBlock, that.bottomBlock)) {
521: return false;
522: }
523: if (!ObjectUtilities.equal(this .leftBlock, that.leftBlock)) {
524: return false;
525: }
526: if (!ObjectUtilities.equal(this .rightBlock, that.rightBlock)) {
527: return false;
528: }
529: if (!ObjectUtilities.equal(this .centerBlock, that.centerBlock)) {
530: return false;
531: }
532: return true;
533: }
534: }
|