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
043package org.jfree.chart.block;
044
045import java.awt.Graphics2D;
046import java.awt.geom.Rectangle2D;
047import java.io.Serializable;
048import java.util.Iterator;
049import java.util.List;
050
051import org.jfree.ui.Size2D;
052
053/**
054 * Arranges blocks in a grid within their container.
055 */
056public 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}