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