    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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    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}