    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     * BorderArrangement.java
    029     * ----------------------
    030     * (C) Copyright 2004-2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes:
    036     * --------
    037     * 22-Oct-2004 : Version 1 (DG);
    038     * 08-Feb-2005 : Updated for changes in RectangleConstraint (DG);
    039     * 24-Feb-2005 : Improved arrangeRR() method (DG);
    040     * 03-May-2005 : Implemented Serializable and added equals() method (DG);
    041     * 13-May-2005 : Fixed bugs in the arrange() method (DG);
    042     * 08-Apr-2008 : Fixed bug in arrangeFF() method where width is too small for
    043     *               left and right blocks (DG);
    044     *
    045     */
    046
    047    package org.jfree.chart.block;
    048
    049    import java.awt.Graphics2D;
    050    import java.awt.geom.Rectangle2D;
    051    import java.io.Serializable;
    052
    053    import org.jfree.data.Range;
    054    import org.jfree.ui.RectangleEdge;
    055    import org.jfree.ui.Size2D;
    056    import org.jfree.util.ObjectUtilities;
    057
    058    /**
    059     * An arrangement manager that lays out blocks in a similar way to
    060     * Swing's BorderLayout class.
    061     */
    062    public class BorderArrangement implements Arrangement, Serializable {
    063
    064        /** For serialization. */
    065        private static final long serialVersionUID = 506071142274883745L;
    066
    067        /** The block (if any) at the center of the layout. */
    068        private Block centerBlock;
    069
    070        /** The block (if any) at the top of the layout. */
    071        private Block topBlock;
    072
    073        /** The block (if any) at the bottom of the layout. */
    074        private Block bottomBlock;
    075
    076        /** The block (if any) at the left of the layout. */
    077        private Block leftBlock;
    078
    079        /** The block (if any) at the right of the layout. */
    080        private Block rightBlock;
    081
    082        /**
    083         * Creates a new instance.
    084         */
    085        public BorderArrangement() {
    086        }
    087
    088        /**
    089         * Adds a block to the arrangement manager at the specified edge.
    090         *
    091         * @param block  the block (<code>null</code> permitted).
    092         * @param key  the edge (an instance of {@link RectangleEdge}) or
    093         *             <code>null</code> for the center block.
    094         */
    095        public void add(Block block, Object key) {
    096
    097            if (key == null) {
    098                this.centerBlock = block;
    099            }
    100            else {
    101                RectangleEdge edge = (RectangleEdge) key;
    102                if (edge == RectangleEdge.TOP) {
    103                    this.topBlock = block;
    104                }
    105                else if (edge == RectangleEdge.BOTTOM) {
    106                    this.bottomBlock = block;
    107                }
    108                else if (edge == RectangleEdge.LEFT) {
    109                    this.leftBlock = block;
    110                }
    111                else if (edge == RectangleEdge.RIGHT) {
    112                    this.rightBlock = block;
    113                }
    114            }
    115        }
    116
    117        /**
    118         * Arranges the items in the specified container, subject to the given
    119         * constraint.
    120         *
    121         * @param container  the container.
    122         * @param g2  the graphics device.
    123         * @param constraint  the constraint.
    124         *
    125         * @return The block size.
    126         */
    127        public Size2D arrange(BlockContainer container,
    128                              Graphics2D g2,
    129                              RectangleConstraint constraint) {
    130            RectangleConstraint contentConstraint
    131                    = container.toContentConstraint(constraint);
    132            Size2D contentSize = null;
    133            LengthConstraintType w = contentConstraint.getWidthConstraintType();
    134            LengthConstraintType h = contentConstraint.getHeightConstraintType();
    135            if (w == LengthConstraintType.NONE) {
    136                if (h == LengthConstraintType.NONE) {
    137                    contentSize = arrangeNN(container, g2);
    138                }
    139                else if (h == LengthConstraintType.FIXED) {
    140                    throw new RuntimeException("Not implemented.");
    141                }
    142                else if (h == LengthConstraintType.RANGE) {
    143                    throw new RuntimeException("Not implemented.");
    144                }
    145            }
    146            else if (w == LengthConstraintType.FIXED) {
    147                if (h == LengthConstraintType.NONE) {
    148                    contentSize = arrangeFN(container, g2, constraint.getWidth());
    149                }
    150                else if (h == LengthConstraintType.FIXED) {
    151                    contentSize = arrangeFF(container, g2, constraint);
    152                }
    153                else if (h == LengthConstraintType.RANGE) {
    154                    contentSize = arrangeFR(container, g2, constraint);
    155                }
    156            }
    157            else if (w == LengthConstraintType.RANGE) {
    158                if (h == LengthConstraintType.NONE) {
    159                    throw new RuntimeException("Not implemented.");
    160                }
    161                else if (h == LengthConstraintType.FIXED) {
    162                    throw new RuntimeException("Not implemented.");
    163                }
    164                else if (h == LengthConstraintType.RANGE) {
    165                    contentSize = arrangeRR(container, constraint.getWidthRange(),
    166                            constraint.getHeightRange(), g2);
    167                }
    168            }
    169            return new Size2D(container.calculateTotalWidth(contentSize.getWidth()),
    170                    container.calculateTotalHeight(contentSize.getHeight()));
    171        }
    172
    173        /**
    174         * Performs an arrangement without constraints.
    175         *
    176         * @param container  the container.
    177         * @param g2  the graphics device.
    178         *
    179         * @return The container size after the arrangement.
    180         */
    181        protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
    182            double[] w = new double[5];
    183            double[] h = new double[5];
    184            if (this.topBlock != null) {
    185                Size2D size = this.topBlock.arrange(g2, RectangleConstraint.NONE);
    186                w[0] = size.width;
    187                h[0] = size.height;
    188            }
    189            if (this.bottomBlock != null) {
    190                Size2D size = this.bottomBlock.arrange(g2,
    191                        RectangleConstraint.NONE);
    192                w[1] = size.width;
    193                h[1] = size.height;
    194            }
    195            if (this.leftBlock != null) {
    196                Size2D size = this.leftBlock.arrange(g2, RectangleConstraint.NONE);
    197                w[2] = size.width;
    198                h[2] = size.height;
    199           }
    200            if (this.rightBlock != null) {
    201                Size2D size = this.rightBlock.arrange(g2, RectangleConstraint.NONE);
    202                w[3] = size.width;
    203                h[3] = size.height;
    204            }
    205
    206            h[2] = Math.max(h[2], h[3]);
    207            h[3] = h[2];
    208
    209            if (this.centerBlock != null) {
    210                Size2D size = this.centerBlock.arrange(g2,
    211                        RectangleConstraint.NONE);
    212                w[4] = size.width;
    213                h[4] = size.height;
    214            }
    215            double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
    216            double centerHeight = Math.max(h[2], Math.max(h[3], h[4]));
    217            double height = h[0] + h[1] + centerHeight;
    218            if (this.topBlock != null) {
    219                this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width,
    220                        h[0]));
    221            }
    222            if (this.bottomBlock != null) {
    223                this.bottomBlock.setBounds(new Rectangle2D.Double(0.0,
    224                        height - h[1], width, h[1]));
    225            }
    226            if (this.leftBlock != null) {
    227                this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2],
    228                        centerHeight));
    229            }
    230            if (this.rightBlock != null) {
    231                this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3],
    232                        h[0], w[3], centerHeight));
    233            }
    234
    235            if (this.centerBlock != null) {
    236                this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0],
    237                        width - w[2] - w[3], centerHeight));
    238            }
    239            return new Size2D(width, height);
    240        }
    241
    242        /**
    243         * Performs an arrangement with a fixed width and a range for the height.
    244         *
    245         * @param container  the container.
    246         * @param g2  the graphics device.
    247         * @param constraint  the constraint.
    248         *
    249         * @return The container size after the arrangement.
    250         */
    251        protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
    252                                   RectangleConstraint constraint) {
    253            Size2D size1 = arrangeFN(container, g2, constraint.getWidth());
    254            if (constraint.getHeightRange().contains(size1.getHeight())) {
    255                return size1;
    256            }
    257            else {
    258                double h = constraint.getHeightRange().constrain(size1.getHeight());
    259                RectangleConstraint c2 = constraint.toFixedHeight(h);
    260                return arrange(container, g2, c2);
    261            }
    262        }
    263
    264        /**
    265         * Arranges the container width a fixed width and no constraint on the
    266         * height.
    267         *
    268         * @param container  the container.
    269         * @param g2  the graphics device.
    270         * @param width  the fixed width.
    271         *
    272         * @return The container size after arranging the contents.
    273         */
    274        protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
    275                                   double width) {
    276            double[] w = new double[5];
    277            double[] h = new double[5];
    278            RectangleConstraint c1 = new RectangleConstraint(width, null,
    279                    LengthConstraintType.FIXED, 0.0, null,
    280                    LengthConstraintType.NONE);
    281            if (this.topBlock != null) {
    282                Size2D size = this.topBlock.arrange(g2, c1);
    283                w[0] = size.width;
    284                h[0] = size.height;
    285            }
    286            if (this.bottomBlock != null) {
    287                Size2D size = this.bottomBlock.arrange(g2, c1);
    288                w[1] = size.width;
    289                h[1] = size.height;
    290            }
    291            RectangleConstraint c2 = new RectangleConstraint(0.0,
    292                    new Range(0.0, width), LengthConstraintType.RANGE,
    293                    0.0, null, LengthConstraintType.NONE);
    294            if (this.leftBlock != null) {
    295                Size2D size = this.leftBlock.arrange(g2, c2);
    296                w[2] = size.width;
    297                h[2] = size.height;
    298            }
    299            if (this.rightBlock != null) {
    300                double maxW = Math.max(width - w[2], 0.0);
    301                RectangleConstraint c3 = new RectangleConstraint(0.0,
    302                        new Range(Math.min(w[2], maxW), maxW),
    303                        LengthConstraintType.RANGE, 0.0, null,
    304                        LengthConstraintType.NONE);
    305                Size2D size = this.rightBlock.arrange(g2, c3);
    306                w[3] = size.width;
    307                h[3] = size.height;
    308            }
    309
    310            h[2] = Math.max(h[2], h[3]);
    311            h[3] = h[2];
    312
    313            if (this.centerBlock != null) {
    314                RectangleConstraint c4 = new RectangleConstraint(width - w[2]
    315                        - w[3], null, LengthConstraintType.FIXED, 0.0, null,
    316                        LengthConstraintType.NONE);
    317                Size2D size = this.centerBlock.arrange(g2, c4);
    318                w[4] = size.width;
    319                h[4] = size.height;
    320            }
    321            double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
    322            return arrange(container, g2, new RectangleConstraint(width, height));
    323        }
    324
    325        /**
    326         * Performs an arrangement with range constraints on both the vertical
    327         * and horizontal sides.
    328         *
    329         * @param container  the container.
    330         * @param widthRange  the allowable range for the container width.
    331         * @param heightRange  the allowable range for the container height.
    332         * @param g2  the graphics device.
    333         *
    334         * @return The container size.
    335         */
    336        protected Size2D arrangeRR(BlockContainer container,
    337                                   Range widthRange, Range heightRange,
    338                                   Graphics2D g2) {
    339            double[] w = new double[5];
    340            double[] h = new double[5];
    341            if (this.topBlock != null) {
    342                RectangleConstraint c1 = new RectangleConstraint(widthRange,
    343                        heightRange);
    344                Size2D size = this.topBlock.arrange(g2, c1);
    345                w[0] = size.width;
    346                h[0] = size.height;
    347            }
    348            if (this.bottomBlock != null) {
    349                Range heightRange2 = Range.shift(heightRange, -h[0], false);
    350                RectangleConstraint c2 = new RectangleConstraint(widthRange,
    351                        heightRange2);
    352                Size2D size = this.bottomBlock.arrange(g2, c2);
    353                w[1] = size.width;
    354                h[1] = size.height;
    355            }
    356            Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1]));
    357            if (this.leftBlock != null) {
    358                RectangleConstraint c3 = new RectangleConstraint(widthRange,
    359                        heightRange3);
    360                Size2D size = this.leftBlock.arrange(g2, c3);
    361                w[2] = size.width;
    362                h[2] = size.height;
    363            }
    364            Range widthRange2 = Range.shift(widthRange, -w[2], false);
    365            if (this.rightBlock != null) {
    366                RectangleConstraint c4 = new RectangleConstraint(widthRange2,
    367                        heightRange3);
    368                Size2D size = this.rightBlock.arrange(g2, c4);
    369                w[3] = size.width;
    370                h[3] = size.height;
    371            }
    372
    373            h[2] = Math.max(h[2], h[3]);
    374            h[3] = h[2];
    375            Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]), false);
    376            if (this.centerBlock != null) {
    377                RectangleConstraint c5 = new RectangleConstraint(widthRange3,
    378                        heightRange3);
    379                Size2D size = this.centerBlock.arrange(g2, c5);
    380                w[4] = size.width;
    381                h[4] = size.height;
    382            }
    383            double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
    384            double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
    385            if (this.topBlock != null) {
    386                this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width,
    387                        h[0]));
    388            }
    389            if (this.bottomBlock != null) {
    390                this.bottomBlock.setBounds(new Rectangle2D.Double(0.0,
    391                        height - h[1], width, h[1]));
    392            }
    393            if (this.leftBlock != null) {
    394                this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2],
    395                        h[2]));
    396            }
    397            if (this.rightBlock != null) {
    398                this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3],
    399                        h[0], w[3], h[3]));
    400            }
    401
    402            if (this.centerBlock != null) {
    403                this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0],
    404                        width - w[2] - w[3], height - h[0] - h[1]));
    405            }
    406            return new Size2D(width, height);
    407        }
    408
    409        /**
    410         * Arranges the items within a container.
    411         *
    412         * @param container  the container.
    413         * @param constraint  the constraint.
    414         * @param g2  the graphics device.
    415         *
    416         * @return The container size after the arrangement.
    417         */
    418        protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
    419                                   RectangleConstraint constraint) {
    420            double[] w = new double[5];
    421            double[] h = new double[5];
    422            w[0] = constraint.getWidth();
    423            if (this.topBlock != null) {
    424                RectangleConstraint c1 = new RectangleConstraint(w[0], null,
    425                        LengthConstraintType.FIXED, 0.0,
    426                        new Range(0.0, constraint.getHeight()),
    427                        LengthConstraintType.RANGE);
    428                Size2D size = this.topBlock.arrange(g2, c1);
    429                h[0] = size.height;
    430            }
    431            w[1] = w[0];
    432            if (this.bottomBlock != null) {
    433                RectangleConstraint c2 = new RectangleConstraint(w[0], null,
    434                        LengthConstraintType.FIXED, 0.0, new Range(0.0,
    435                        constraint.getHeight() - h[0]), LengthConstraintType.RANGE);
    436                Size2D size = this.bottomBlock.arrange(g2, c2);
    437                h[1] = size.height;
    438            }
    439            h[2] = constraint.getHeight() - h[1] - h[0];
    440            if (this.leftBlock != null) {
    441                RectangleConstraint c3 = new RectangleConstraint(0.0,
    442                        new Range(0.0, constraint.getWidth()),
    443                        LengthConstraintType.RANGE, h[2], null,
    444                        LengthConstraintType.FIXED);
    445                Size2D size = this.leftBlock.arrange(g2, c3);
    446                w[2] = size.width;
    447            }
    448            h[3] = h[2];
    449            if (this.rightBlock != null) {
    450                RectangleConstraint c4 = new RectangleConstraint(0.0,
    451                        new Range(0.0, Math.max(constraint.getWidth() - w[2], 0.0)),
    452                        LengthConstraintType.RANGE, h[2], null,
    453                        LengthConstraintType.FIXED);
    454                Size2D size = this.rightBlock.arrange(g2, c4);
    455                w[3] = size.width;
    456            }
    457            h[4] = h[2];
    458            w[4] = constraint.getWidth() - w[3] - w[2];
    459            RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]);
    460            if (this.centerBlock != null) {
    461                this.centerBlock.arrange(g2, c5);
    462            }
    463
    464            if (this.topBlock != null) {
    465                this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, w[0],
    466                        h[0]));
    467            }
    468            if (this.bottomBlock != null) {
    469                this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, h[0] + h[2],
    470                        w[1], h[1]));
    471            }
    472            if (this.leftBlock != null) {
    473                this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2],
    474                        h[2]));
    475            }
    476            if (this.rightBlock != null) {
    477                this.rightBlock.setBounds(new Rectangle2D.Double(w[2] + w[4], h[0],
    478                        w[3], h[3]));
    479            }
    480            if (this.centerBlock != null) {
    481                this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], w[4],
    482                        h[4]));
    483            }
    484            return new Size2D(constraint.getWidth(), constraint.getHeight());
    485        }
    486
    487        /**
    488         * Clears the layout.
    489         */
    490        public void clear() {
    491            this.centerBlock = null;
    492            this.topBlock = null;
    493            this.bottomBlock = null;
    494            this.leftBlock = null;
    495            this.rightBlock = null;
    496        }
    497
    498        /**
    499         * Tests this arrangement for equality with an arbitrary object.
    500         *
    501         * @param obj  the object (<code>null</code> permitted).
    502         *
    503         * @return A boolean.
    504         */
    505        public boolean equals(Object obj) {
    506            if (obj == this) {
    507                return true;
    508            }
    509            if (!(obj instanceof BorderArrangement)) {
    510                return false;
    511            }
    512            BorderArrangement that = (BorderArrangement) obj;
    513            if (!ObjectUtilities.equal(this.topBlock, that.topBlock)) {
    514                return false;
    515            }
    516            if (!ObjectUtilities.equal(this.bottomBlock, that.bottomBlock)) {
    517                return false;
    518            }
    519            if (!ObjectUtilities.equal(this.leftBlock, that.leftBlock)) {
    520                return false;
    521            }
    522            if (!ObjectUtilities.equal(this.rightBlock, that.rightBlock)) {
    523                return false;
    524            }
    525            if (!ObjectUtilities.equal(this.centerBlock, that.centerBlock)) {
    526                return false;
    527            }
    528            return true;
    529        }
    530    }