    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
    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    }