001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
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    @Override
088    public void add(Block block, Object key) {
089        // can safely ignore
090    }
091
092    /**
093     * Arranges the blocks within the specified container, subject to the given
094     * constraint.
095     *
096     * @param container  the container (<code>null</code> not permitted).
097     * @param constraint  the constraint.
098     * @param g2  the graphics device.
099     *
100     * @return The size following the arrangement.
101     */
102    @Override
103    public Size2D arrange(BlockContainer container, Graphics2D g2,
104                          RectangleConstraint constraint) {
105        LengthConstraintType w = constraint.getWidthConstraintType();
106        LengthConstraintType h = constraint.getHeightConstraintType();
107        if (w == LengthConstraintType.NONE) {
108            if (h == LengthConstraintType.NONE) {
109                return arrangeNN(container, g2);
110            }
111            else if (h == LengthConstraintType.FIXED) {
112                return arrangeNF(container, g2, constraint);
113            }
114            else if (h == LengthConstraintType.RANGE) {
115                // find optimum height, then map to range
116                return arrangeNR(container, g2, constraint);
117            }
118        }
119        else if (w == LengthConstraintType.FIXED) {
120            if (h == LengthConstraintType.NONE) {
121                // find optimum height
122                return arrangeFN(container, g2, constraint);
123            }
124            else if (h == LengthConstraintType.FIXED) {
125                return arrangeFF(container, g2, constraint);
126            }
127            else if (h == LengthConstraintType.RANGE) {
128                // find optimum height and map to range
129                return arrangeFR(container, g2, constraint);
130            }
131        }
132        else if (w == LengthConstraintType.RANGE) {
133            // find optimum width and map to range
134            if (h == LengthConstraintType.NONE) {
135                // find optimum height
136                return arrangeRN(container, g2, constraint);
137            }
138            else if (h == LengthConstraintType.FIXED) {
139                // fixed width
140                return arrangeRF(container, g2, constraint);
141            }
142            else if (h == LengthConstraintType.RANGE) {
143                return arrangeRR(container, g2, constraint);
144            }
145        }
146        throw new RuntimeException("Should never get to here!");
147    }
148
149    /**
150     * Arranges the container with no constraint on the width or height.
151     *
152     * @param container  the container (<code>null</code> not permitted).
153     * @param g2  the graphics device.
154     *
155     * @return The size.
156     */
157    protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
158        double maxW = 0.0;
159        double maxH = 0.0;
160        List blocks = container.getBlocks();
161        Iterator iterator = blocks.iterator();
162        while (iterator.hasNext()) {
163            Block b = (Block) iterator.next();
164            if (b != null) {
165                Size2D s = b.arrange(g2, RectangleConstraint.NONE);
166                maxW = Math.max(maxW, s.width);
167                maxH = Math.max(maxH, s.height);
168            }
169        }
170        double width = this.columns * maxW;
171        double height = this.rows * maxH;
172        RectangleConstraint c = new RectangleConstraint(width, height);
173        return arrangeFF(container, g2, c);
174    }
175
176    /**
177     * Arranges the container with a fixed overall width and height.
178     *
179     * @param container  the container (<code>null</code> not permitted).
180     * @param g2  the graphics device.
181     * @param constraint  the constraint (<code>null</code> not permitted).
182     *
183     * @return The size following the arrangement.
184     */
185    protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
186                               RectangleConstraint constraint) {
187        double width = constraint.getWidth() / this.columns;
188        double height = constraint.getHeight() / this.rows;
189        List blocks = container.getBlocks();
190        for (int c = 0; c < this.columns; c++) {
191            for (int r = 0; r < this.rows; r++) {
192                int index = r * this.columns + c;
193                if (index >= blocks.size()) {
194                    break;
195                }
196                Block b = (Block) blocks.get(index);
197                if (b != null) {
198                    b.setBounds(new Rectangle2D.Double(c * width, r * height,
199                            width, height));
200                }
201            }
202        }
203        return new Size2D(this.columns * width, this.rows * height);
204    }
205
206    /**
207     * Arrange with a fixed width and a height within a given range.
208     *
209     * @param container  the container.
210     * @param constraint  the constraint.
211     * @param g2  the graphics device.
212     *
213     * @return The size of the arrangement.
214     */
215    protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
216                               RectangleConstraint constraint) {
217
218        RectangleConstraint c1 = constraint.toUnconstrainedHeight();
219        Size2D size1 = arrange(container, g2, c1);
220
221        if (constraint.getHeightRange().contains(size1.getHeight())) {
222            return size1;
223        }
224        else {
225            double h = constraint.getHeightRange().constrain(size1.getHeight());
226            RectangleConstraint c2 = constraint.toFixedHeight(h);
227            return arrange(container, g2, c2);
228        }
229    }
230
231    /**
232     * Arrange with a fixed height and a width within a given range.
233     *
234     * @param container  the container.
235     * @param constraint  the constraint.
236     * @param g2  the graphics device.
237     *
238     * @return The size of the arrangement.
239     */
240    protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
241                               RectangleConstraint constraint) {
242
243        RectangleConstraint c1 = constraint.toUnconstrainedWidth();
244        Size2D size1 = arrange(container, g2, c1);
245
246        if (constraint.getWidthRange().contains(size1.getWidth())) {
247            return size1;
248        }
249        else {
250            double w = constraint.getWidthRange().constrain(size1.getWidth());
251            RectangleConstraint c2 = constraint.toFixedWidth(w);
252            return arrange(container, g2, c2);
253        }
254    }
255
256    /**
257     * Arrange with a fixed width and no height constraint.
258     *
259     * @param container  the container.
260     * @param constraint  the constraint.
261     * @param g2  the graphics device.
262     *
263     * @return The size of the arrangement.
264     */
265    protected Size2D arrangeRN(BlockContainer container, Graphics2D g2,
266                               RectangleConstraint constraint) {
267
268        RectangleConstraint c1 = constraint.toUnconstrainedWidth();
269        Size2D size1 = arrange(container, g2, c1);
270
271        if (constraint.getWidthRange().contains(size1.getWidth())) {
272            return size1;
273        }
274        else {
275            double w = constraint.getWidthRange().constrain(size1.getWidth());
276            RectangleConstraint c2 = constraint.toFixedWidth(w);
277            return arrange(container, g2, c2);
278        }
279    }
280
281    /**
282     * Arrange with a fixed height and no width constraint.
283     *
284     * @param container  the container.
285     * @param constraint  the constraint.
286     * @param g2  the graphics device.
287     *
288     * @return The size of the arrangement.
289     */
290    protected Size2D arrangeNR(BlockContainer container, Graphics2D g2,
291                               RectangleConstraint constraint) {
292
293        RectangleConstraint c1 = constraint.toUnconstrainedHeight();
294        Size2D size1 = arrange(container, g2, c1);
295
296        if (constraint.getHeightRange().contains(size1.getHeight())) {
297            return size1;
298        }
299        else {
300            double h = constraint.getHeightRange().constrain(size1.getHeight());
301            RectangleConstraint c2 = constraint.toFixedHeight(h);
302            return arrange(container, g2, c2);
303        }
304    }
305
306    /**
307     * Arrange with ranges for both the width and height constraints.
308     *
309     * @param container  the container.
310     * @param constraint  the constraint.
311     * @param g2  the graphics device.
312     *
313     * @return The size of the arrangement.
314     */
315    protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
316                               RectangleConstraint constraint) {
317
318        Size2D size1 = arrange(container, g2, RectangleConstraint.NONE);
319
320        if (constraint.getWidthRange().contains(size1.getWidth())) {
321            if (constraint.getHeightRange().contains(size1.getHeight())) {
322                return size1;
323            }
324            else {
325                // width is OK, but height must be constrained
326                double h = constraint.getHeightRange().constrain(
327                        size1.getHeight());
328                RectangleConstraint cc = new RectangleConstraint(
329                        size1.getWidth(), h);
330                return arrangeFF(container, g2, cc);
331            }
332        }
333        else {
334            if (constraint.getHeightRange().contains(size1.getHeight())) {
335                // height is OK, but width must be constrained
336                double w = constraint.getWidthRange().constrain(
337                        size1.getWidth());
338                RectangleConstraint cc = new RectangleConstraint(w,
339                        size1.getHeight());
340                return arrangeFF(container, g2, cc);
341
342            }
343            else {
344                double w = constraint.getWidthRange().constrain(
345                        size1.getWidth());
346                double h = constraint.getHeightRange().constrain(
347                        size1.getHeight());
348                RectangleConstraint cc = new RectangleConstraint(w, h);
349                return arrangeFF(container, g2, cc);
350            }
351        }
352    }
353
354    /**
355     * Arrange with a fixed width and a height within a given range.
356     *
357     * @param container  the container.
358     * @param g2  the graphics device.
359     * @param constraint  the constraint.
360     *
361     * @return The size of the arrangement.
362     */
363    protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
364                               RectangleConstraint constraint) {
365
366        double width = constraint.getWidth() / this.columns;
367        RectangleConstraint bc = constraint.toFixedWidth(width);
368        List blocks = container.getBlocks();
369        double maxH = 0.0;
370        for (int r = 0; r < this.rows; r++) {
371            for (int c = 0; c < this.columns; c++) {
372                int index = r * this.columns + c;
373                if (index >= blocks.size()) {
374                    break;
375                }
376                Block b = (Block) blocks.get(index);
377                if (b != null) {
378                    Size2D s = b.arrange(g2, bc);
379                    maxH = Math.max(maxH, s.getHeight());
380                }
381            }
382        }
383        RectangleConstraint cc = constraint.toFixedHeight(maxH * this.rows);
384        return arrange(container, g2, cc);
385    }
386
387    /**
388     * Arrange with a fixed height and no constraint for the width.
389     *
390     * @param container  the container.
391     * @param g2  the graphics device.
392     * @param constraint  the constraint.
393     *
394     * @return The size of the arrangement.
395     */
396    protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
397                               RectangleConstraint constraint) {
398
399        double height = constraint.getHeight() / this.rows;
400        RectangleConstraint bc = constraint.toFixedHeight(height);
401        List blocks = container.getBlocks();
402        double maxW = 0.0;
403        for (int r = 0; r < this.rows; r++) {
404            for (int c = 0; c < this.columns; c++) {
405                int index = r * this.columns + c;
406                if (index >= blocks.size()) {
407                    break;
408                }
409                Block b = (Block) blocks.get(index);
410                if (b != null) {
411                    Size2D s = b.arrange(g2, bc);
412                    maxW = Math.max(maxW, s.getWidth());
413                }
414            }
415        }
416        RectangleConstraint cc = constraint.toFixedWidth(maxW * this.columns);
417        return arrange(container, g2, cc);
418    }
419
420    /**
421     * Clears any cached layout information retained by the arrangement.
422     */
423    @Override
424    public void clear() {
425        // nothing to clear
426    }
427
428    /**
429     * Compares this layout manager for equality with an arbitrary object.
430     *
431     * @param obj  the object.
432     *
433     * @return A boolean.
434     */
435    @Override
436    public boolean equals(Object obj) {
437        if (obj == this) {
438            return true;
439        }
440        if (!(obj instanceof GridArrangement)) {
441            return false;
442        }
443        GridArrangement that = (GridArrangement) obj;
444        if (this.columns != that.columns) {
445            return false;
446        }
447        if (this.rows != that.rows) {
448            return false;
449        }
450        return true;
451    }
452
453}