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