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 * GridArrangement.java
029 * --------------------
030 * (C) Copyright 2005-2008, by Object Refinery Limited.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): -;
034 *
035 * Changes:
036 * --------
037 * 08-Feb-2005 : Version 1 (DG);
038 * 03-Dec-2008 : Implemented missing methods, and fixed bugs reported in
039 * patch 2370487 (DG);
040 *
041 */
042
043 package org.jfree.chart.block;
044
045 import java.awt.Graphics2D;
046 import java.awt.geom.Rectangle2D;
047 import java.io.Serializable;
048 import java.util.Iterator;
049 import java.util.List;
050
051 import org.jfree.ui.Size2D;
052
053 /**
054 * Arranges blocks in a grid within their container.
055 */
056 public class GridArrangement implements Arrangement, Serializable {
057
058 /** For serialization. */
059 private static final long serialVersionUID = -2563758090144655938L;
060
061 /** The rows. */
062 private int rows;
063
064 /** The columns. */
065 private int columns;
066
067 /**
068 * Creates a new grid arrangement.
069 *
070 * @param rows the row count.
071 * @param columns the column count.
072 */
073 public GridArrangement(int rows, int columns) {
074 this.rows = rows;
075 this.columns = columns;
076 }
077
078 /**
079 * Adds a block and a key which can be used to determine the position of
080 * the block in the arrangement. This method is called by the container
081 * (you don't need to call this method directly) and gives the arrangement
082 * an opportunity to record the details if they are required.
083 *
084 * @param block the block.
085 * @param key the key (<code>null</code> permitted).
086 */
087 public void add(Block block, Object key) {
088 // can safely ignore
089 }
090
091 /**
092 * Arranges the blocks within the specified container, subject to the given
093 * constraint.
094 *
095 * @param container the container (<code>null</code> not permitted).
096 * @param constraint the constraint.
097 * @param g2 the graphics device.
098 *
099 * @return The size following the arrangement.
100 */
101 public Size2D arrange(BlockContainer container, Graphics2D g2,
102 RectangleConstraint constraint) {
103 LengthConstraintType w = constraint.getWidthConstraintType();
104 LengthConstraintType h = constraint.getHeightConstraintType();
105 if (w == LengthConstraintType.NONE) {
106 if (h == LengthConstraintType.NONE) {
107 return arrangeNN(container, g2);
108 }
109 else if (h == LengthConstraintType.FIXED) {
110 return arrangeNF(container, g2, constraint);
111 }
112 else if (h == LengthConstraintType.RANGE) {
113 // find optimum height, then map to range
114 return arrangeNR(container, g2, constraint);
115 }
116 }
117 else if (w == LengthConstraintType.FIXED) {
118 if (h == LengthConstraintType.NONE) {
119 // find optimum height
120 return arrangeFN(container, g2, constraint);
121 }
122 else if (h == LengthConstraintType.FIXED) {
123 return arrangeFF(container, g2, constraint);
124 }
125 else if (h == LengthConstraintType.RANGE) {
126 // find optimum height and map to range
127 return arrangeFR(container, g2, constraint);
128 }
129 }
130 else if (w == LengthConstraintType.RANGE) {
131 // find optimum width and map to range
132 if (h == LengthConstraintType.NONE) {
133 // find optimum height
134 return arrangeRN(container, g2, constraint);
135 }
136 else if (h == LengthConstraintType.FIXED) {
137 // fixed width
138 return arrangeRF(container, g2, constraint);
139 }
140 else if (h == LengthConstraintType.RANGE) {
141 return arrangeRR(container, g2, constraint);
142 }
143 }
144 throw new RuntimeException("Should never get to here!");
145 }
146
147 /**
148 * Arranges the container with no constraint on the width or height.
149 *
150 * @param container the container (<code>null</code> not permitted).
151 * @param g2 the graphics device.
152 *
153 * @return The size.
154 */
155 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
156 double maxW = 0.0;
157 double maxH = 0.0;
158 List blocks = container.getBlocks();
159 Iterator iterator = blocks.iterator();
160 while (iterator.hasNext()) {
161 Block b = (Block) iterator.next();
162 if (b != null) {
163 Size2D s = b.arrange(g2, RectangleConstraint.NONE);
164 maxW = Math.max(maxW, s.width);
165 maxH = Math.max(maxH, s.height);
166 }
167 }
168 double width = this.columns * maxW;
169 double height = this.rows * maxH;
170 RectangleConstraint c = new RectangleConstraint(width, height);
171 return arrangeFF(container, g2, c);
172 }
173
174 /**
175 * Arranges the container with a fixed overall width and height.
176 *
177 * @param container the container (<code>null</code> not permitted).
178 * @param g2 the graphics device.
179 * @param constraint the constraint (<code>null</code> not permitted).
180 *
181 * @return The size following the arrangement.
182 */
183 protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
184 RectangleConstraint constraint) {
185 double width = constraint.getWidth() / this.columns;
186 double height = constraint.getHeight() / this.rows;
187 List blocks = container.getBlocks();
188 for (int c = 0; c < this.columns; c++) {
189 for (int r = 0; r < this.rows; r++) {
190 int index = r * this.columns + c;
191 if (index >= blocks.size()) {
192 break;
193 }
194 Block b = (Block) blocks.get(index);
195 if (b != null) {
196 b.setBounds(new Rectangle2D.Double(c * width, r * height,
197 width, height));
198 }
199 }
200 }
201 return new Size2D(this.columns * width, this.rows * height);
202 }
203
204 /**
205 * Arrange with a fixed width and a height within a given range.
206 *
207 * @param container the container.
208 * @param constraint the constraint.
209 * @param g2 the graphics device.
210 *
211 * @return The size of the arrangement.
212 */
213 protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
214 RectangleConstraint constraint) {
215
216 RectangleConstraint c1 = constraint.toUnconstrainedHeight();
217 Size2D size1 = arrange(container, g2, c1);
218
219 if (constraint.getHeightRange().contains(size1.getHeight())) {
220 return size1;
221 }
222 else {
223 double h = constraint.getHeightRange().constrain(size1.getHeight());
224 RectangleConstraint c2 = constraint.toFixedHeight(h);
225 return arrange(container, g2, c2);
226 }
227 }
228
229 /**
230 * Arrange with a fixed height and a width within a given range.
231 *
232 * @param container the container.
233 * @param constraint the constraint.
234 * @param g2 the graphics device.
235 *
236 * @return The size of the arrangement.
237 */
238 protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
239 RectangleConstraint constraint) {
240
241 RectangleConstraint c1 = constraint.toUnconstrainedWidth();
242 Size2D size1 = arrange(container, g2, c1);
243
244 if (constraint.getWidthRange().contains(size1.getWidth())) {
245 return size1;
246 }
247 else {
248 double w = constraint.getWidthRange().constrain(size1.getWidth());
249 RectangleConstraint c2 = constraint.toFixedWidth(w);
250 return arrange(container, g2, c2);
251 }
252 }
253
254 /**
255 * Arrange with a fixed width and no height constraint.
256 *
257 * @param container the container.
258 * @param constraint the constraint.
259 * @param g2 the graphics device.
260 *
261 * @return The size of the arrangement.
262 */
263 protected Size2D arrangeRN(BlockContainer container, Graphics2D g2,
264 RectangleConstraint constraint) {
265
266 RectangleConstraint c1 = constraint.toUnconstrainedWidth();
267 Size2D size1 = arrange(container, g2, c1);
268
269 if (constraint.getWidthRange().contains(size1.getWidth())) {
270 return size1;
271 }
272 else {
273 double w = constraint.getWidthRange().constrain(size1.getWidth());
274 RectangleConstraint c2 = constraint.toFixedWidth(w);
275 return arrange(container, g2, c2);
276 }
277 }
278
279 /**
280 * Arrange with a fixed height and no width constraint.
281 *
282 * @param container the container.
283 * @param constraint the constraint.
284 * @param g2 the graphics device.
285 *
286 * @return The size of the arrangement.
287 */
288 protected Size2D arrangeNR(BlockContainer container, Graphics2D g2,
289 RectangleConstraint constraint) {
290
291 RectangleConstraint c1 = constraint.toUnconstrainedHeight();
292 Size2D size1 = arrange(container, g2, c1);
293
294 if (constraint.getHeightRange().contains(size1.getHeight())) {
295 return size1;
296 }
297 else {
298 double h = constraint.getHeightRange().constrain(size1.getHeight());
299 RectangleConstraint c2 = constraint.toFixedHeight(h);
300 return arrange(container, g2, c2);
301 }
302 }
303
304 /**
305 * Arrange with ranges for both the width and height constraints.
306 *
307 * @param container the container.
308 * @param constraint the constraint.
309 * @param g2 the graphics device.
310 *
311 * @return The size of the arrangement.
312 */
313 protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
314 RectangleConstraint constraint) {
315
316 Size2D size1 = arrange(container, g2, RectangleConstraint.NONE);
317
318 if (constraint.getWidthRange().contains(size1.getWidth())) {
319 if (constraint.getHeightRange().contains(size1.getHeight())) {
320 return size1;
321 }
322 else {
323 // width is OK, but height must be constrained
324 double h = constraint.getHeightRange().constrain(
325 size1.getHeight());
326 RectangleConstraint cc = new RectangleConstraint(
327 size1.getWidth(), h);
328 return arrangeFF(container, g2, cc);
329 }
330 }
331 else {
332 if (constraint.getHeightRange().contains(size1.getHeight())) {
333 // height is OK, but width must be constrained
334 double w = constraint.getWidthRange().constrain(
335 size1.getWidth());
336 RectangleConstraint cc = new RectangleConstraint(w,
337 size1.getHeight());
338 return arrangeFF(container, g2, cc);
339
340 }
341 else {
342 double w = constraint.getWidthRange().constrain(
343 size1.getWidth());
344 double h = constraint.getHeightRange().constrain(
345 size1.getHeight());
346 RectangleConstraint cc = new RectangleConstraint(w, h);
347 return arrangeFF(container, g2, cc);
348 }
349 }
350 }
351
352 /**
353 * Arrange with a fixed width and a height within a given range.
354 *
355 * @param container the container.
356 * @param g2 the graphics device.
357 * @param constraint the constraint.
358 *
359 * @return The size of the arrangement.
360 */
361 protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
362 RectangleConstraint constraint) {
363
364 double width = constraint.getWidth() / this.columns;
365 RectangleConstraint bc = constraint.toFixedWidth(width);
366 List blocks = container.getBlocks();
367 double maxH = 0.0;
368 for (int r = 0; r < this.rows; r++) {
369 for (int c = 0; c < this.columns; c++) {
370 int index = r * this.columns + c;
371 if (index >= blocks.size()) {
372 break;
373 }
374 Block b = (Block) blocks.get(index);
375 if (b != null) {
376 Size2D s = b.arrange(g2, bc);
377 maxH = Math.max(maxH, s.getHeight());
378 }
379 }
380 }
381 RectangleConstraint cc = constraint.toFixedHeight(maxH * this.rows);
382 return arrange(container, g2, cc);
383 }
384
385 /**
386 * Arrange with a fixed height and no constraint for the width.
387 *
388 * @param container the container.
389 * @param g2 the graphics device.
390 * @param constraint the constraint.
391 *
392 * @return The size of the arrangement.
393 */
394 protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
395 RectangleConstraint constraint) {
396
397 double height = constraint.getHeight() / this.rows;
398 RectangleConstraint bc = constraint.toFixedHeight(height);
399 List blocks = container.getBlocks();
400 double maxW = 0.0;
401 for (int r = 0; r < this.rows; r++) {
402 for (int c = 0; c < this.columns; c++) {
403 int index = r * this.columns + c;
404 if (index >= blocks.size()) {
405 break;
406 }
407 Block b = (Block) blocks.get(index);
408 if (b != null) {
409 Size2D s = b.arrange(g2, bc);
410 maxW = Math.max(maxW, s.getWidth());
411 }
412 }
413 }
414 RectangleConstraint cc = constraint.toFixedWidth(maxW * this.columns);
415 return arrange(container, g2, cc);
416 }
417
418 /**
419 * Clears any cached layout information retained by the arrangement.
420 */
421 public void clear() {
422 // nothing to clear
423 }
424
425 /**
426 * Compares this layout manager for equality with an arbitrary object.
427 *
428 * @param obj the object.
429 *
430 * @return A boolean.
431 */
432 public boolean equals(Object obj) {
433 if (obj == this) {
434 return true;
435 }
436 if (!(obj instanceof GridArrangement)) {
437 return false;
438 }
439 GridArrangement that = (GridArrangement) obj;
440 if (this.columns != that.columns) {
441 return false;
442 }
443 if (this.rows != that.rows) {
444 return false;
445 }
446 return true;
447 }
448
449 }