    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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, 2005, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: BorderArrangement.java,v 1.14.2.1 2005/10/25 20:39:38 mungady Exp $
    036     *
    037     * Changes:
    038     * --------
    039     * 22-Oct-2004 : Version 1 (DG);
    040     * 08-Feb-2005 : Updated for changes in RectangleConstraint (DG);
    041     * 24-Feb-2005 : Improved arrangeRR() method (DG);
    042     * 03-May-2005 : Implemented Serializable and added equals() method (DG);
    043     * 13-May-2005 : Fixed bugs in the arrange() method (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(
    166                        container, constraint.getWidthRange(),
    167                        constraint.getHeightRange(), g2
    168                    );
    169                }
    170            }
    171            return new Size2D(
    172                container.calculateTotalWidth(contentSize.getWidth()),
    173                container.calculateTotalHeight(contentSize.getHeight())
    174            );
    175        }
    176
    177        /**
    178         * Performs an arrangement without constraints.
    179         *
    180         * @param container  the container.
    181         * @param g2  the graphics device.
    182         *
    183         * @return The container size after the arrangement.
    184         */
    185        protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
    186            double[] w = new double[5];
    187            double[] h = new double[5];
    188            if (this.topBlock != null) {
    189                Size2D size = this.topBlock.arrange(
    190                    g2, RectangleConstraint.NONE
    191                );
    192                w[0] = size.width;
    193                h[0] = size.height;
    194            }
    195            if (this.bottomBlock != null) {
    196                Size2D size = this.bottomBlock.arrange(
    197                    g2, RectangleConstraint.NONE
    198                );
    199                w[1] = size.width;
    200                h[1] = size.height;
    201            }
    202            if (this.leftBlock != null) {
    203                Size2D size = this.leftBlock.arrange(
    204                    g2, RectangleConstraint.NONE
    205                );
    206                w[2] = size.width;
    207                h[2] = size.height;
    208           }
    209            if (this.rightBlock != null) {
    210                Size2D size = this.rightBlock.arrange(
    211                    g2, RectangleConstraint.NONE
    212                );
    213                w[3] = size.width;
    214                h[3] = size.height;
    215            }
    216
    217            h[2] = Math.max(h[2], h[3]);
    218            h[3] = h[2];
    219
    220            if (this.centerBlock != null) {
    221                Size2D size = this.centerBlock.arrange(
    222                    g2, RectangleConstraint.NONE
    223                );
    224                w[4] = size.width;
    225                h[4] = size.height;
    226            }
    227            double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
    228            double centerHeight = Math.max(h[2], Math.max(h[3], h[4]));
    229            double height = h[0] + h[1] + centerHeight;
    230            if (this.topBlock != null) {
    231                this.topBlock.setBounds(
    232                    new Rectangle2D.Double(0.0, 0.0, width, h[0])
    233                );
    234            }
    235            if (this.bottomBlock != null) {
    236                this.bottomBlock.setBounds(
    237                    new Rectangle2D.Double(0.0, height - h[1], width, h[1])
    238                );
    239            }
    240            if (this.leftBlock != null) {
    241                this.leftBlock.setBounds(
    242                    new Rectangle2D.Double(0.0, h[0], w[2], centerHeight)
    243                );
    244            }
    245            if (this.rightBlock != null) {
    246                this.rightBlock.setBounds(
    247                    new Rectangle2D.Double(width - w[3], h[0], w[3], centerHeight)
    248                );
    249            }
    250
    251            if (this.centerBlock != null) {
    252                this.centerBlock.setBounds(
    253                    new Rectangle2D.Double(
    254                        w[2], h[0], width - w[2] - w[3], centerHeight
    255                    )
    256                );
    257            }
    258            return new Size2D(width, height);
    259        }
    260
    261        /**
    262         * Performs an arrangement with a fixed width and a range for the height.
    263         *
    264         * @param container  the container.
    265         * @param g2  the graphics device.
    266         * @param constraint  the constraint.
    267         *
    268         * @return The container size after the arrangement.
    269         */
    270        protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
    271                                   RectangleConstraint constraint) {
    272            Size2D size1 = arrangeFN(container, g2, constraint.getWidth());
    273            if (constraint.getHeightRange().contains(size1.getHeight())) {
    274                return size1;
    275            }
    276            else {
    277                double h = constraint.getHeightRange().constrain(size1.getHeight());
    278                RectangleConstraint c2 = constraint.toFixedHeight(h);
    279                return arrange(container, g2, c2);
    280            }
    281        }
    282
    283        /**
    284         * Arranges the container width a fixed width and no constraint on the
    285         * height.
    286         *
    287         * @param container  the container.
    288         * @param g2  the graphics device.
    289         * @param width  the fixed width.
    290         *
    291         * @return The container size after arranging the contents.
    292         */
    293        protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
    294                                   double width) {
    295            double[] w = new double[5];
    296            double[] h = new double[5];
    297            RectangleConstraint c1 = new RectangleConstraint(
    298                width, null, LengthConstraintType.FIXED,
    299                0.0, null, LengthConstraintType.NONE
    300            );
    301            if (this.topBlock != null) {
    302                Size2D size = this.topBlock.arrange(g2, c1);
    303                w[0] = size.width;
    304                h[0] = size.height;
    305            }
    306            if (this.bottomBlock != null) {
    307                Size2D size = this.bottomBlock.arrange(g2, c1);
    308                w[1] = size.width;
    309                h[1] = size.height;
    310            }
    311            RectangleConstraint c2 = new RectangleConstraint(
    312                0.0, new Range(0.0, width), LengthConstraintType.RANGE,
    313                0.0, null, LengthConstraintType.NONE
    314            );
    315            if (this.leftBlock != null) {
    316                Size2D size = this.leftBlock.arrange(g2, c2);
    317                w[2] = size.width;
    318                h[2] = size.height;
    319            }
    320            if (this.rightBlock != null) {
    321                double maxW = Math.max(width - w[2], 0.0);
    322                RectangleConstraint c3 = new RectangleConstraint(
    323                    0.0, new Range(Math.min(w[2], maxW), maxW),
    324                    LengthConstraintType.RANGE,
    325                    0.0, null, LengthConstraintType.NONE
    326                );
    327                Size2D size = this.rightBlock.arrange(g2, c3);
    328                w[3] = size.width;
    329                h[3] = size.height;
    330            }
    331
    332            h[2] = Math.max(h[2], h[3]);
    333            h[3] = h[2];
    334
    335            if (this.centerBlock != null) {
    336                RectangleConstraint c4 = new RectangleConstraint(
    337                    width - w[2] - w[3], null, LengthConstraintType.FIXED,
    338                    0.0, null, LengthConstraintType.NONE
    339                );
    340                Size2D size = this.centerBlock.arrange(g2, c4);
    341                w[4] = size.width;
    342                h[4] = size.height;
    343            }
    344            double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
    345            return arrange(container, g2, new RectangleConstraint(width, height));
    346        }
    347
    348        /**
    349         * Performs an arrangement with range constraints on both the vertical
    350         * and horizontal sides.
    351         *
    352         * @param container  the container.
    353         * @param widthRange  the allowable range for the container width.
    354         * @param heightRange  the allowable range for the container height.
    355         * @param g2  the graphics device.
    356         *
    357         * @return The container size.
    358         */
    359        protected Size2D arrangeRR(BlockContainer container,
    360                                   Range widthRange, Range heightRange,
    361                                   Graphics2D g2) {
    362            double[] w = new double[5];
    363            double[] h = new double[5];
    364            if (this.topBlock != null) {
    365                RectangleConstraint c1 = new RectangleConstraint(
    366                    widthRange, heightRange
    367                );
    368                Size2D size = this.topBlock.arrange(g2, c1);
    369                w[0] = size.width;
    370                h[0] = size.height;
    371            }
    372            if (this.bottomBlock != null) {
    373                Range heightRange2 = Range.shift(heightRange, -h[0], false);
    374                RectangleConstraint c2 = new RectangleConstraint(
    375                    widthRange, heightRange2
    376                );
    377                Size2D size = this.bottomBlock.arrange(g2, c2);
    378                w[1] = size.width;
    379                h[1] = size.height;
    380            }
    381            Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1]));
    382            if (this.leftBlock != null) {
    383                RectangleConstraint c3 = new RectangleConstraint(
    384                    widthRange, heightRange3
    385                );
    386                Size2D size = this.leftBlock.arrange(g2, c3);
    387                w[2] = size.width;
    388                h[2] = size.height;
    389            }
    390            Range widthRange2 = Range.shift(widthRange, -w[2], false);
    391            if (this.rightBlock != null) {
    392                RectangleConstraint c4 = new RectangleConstraint(
    393                    widthRange2, heightRange3
    394                );
    395                Size2D size = this.rightBlock.arrange(g2, c4);
    396                w[3] = size.width;
    397                h[3] = size.height;
    398            }
    399
    400            h[2] = Math.max(h[2], h[3]);
    401            h[3] = h[2];
    402            Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]), false);
    403            if (this.centerBlock != null) {
    404                RectangleConstraint c5 = new RectangleConstraint(
    405                    widthRange3, heightRange3
    406                );
    407                // TODO:  the width and height ranges should be reduced by the
    408                // height required for the top and bottom, and the width required
    409                // by the left and right
    410                Size2D size = this.centerBlock.arrange(g2, c5);
    411                w[4] = size.width;
    412                h[4] = size.height;
    413            }
    414            double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3]));
    415            double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4]));
    416            if (this.topBlock != null) {
    417                this.topBlock.setBounds(
    418                    new Rectangle2D.Double(0.0, 0.0, width, h[0])
    419                );
    420            }
    421            if (this.bottomBlock != null) {
    422                this.bottomBlock.setBounds(
    423                    new Rectangle2D.Double(0.0, height - h[1], width, h[1])
    424                );
    425            }
    426            if (this.leftBlock != null) {
    427                this.leftBlock.setBounds(
    428                    new Rectangle2D.Double(0.0, h[0], w[2], h[2])
    429                );
    430            }
    431            if (this.rightBlock != null) {
    432                this.rightBlock.setBounds(
    433                    new Rectangle2D.Double(width - w[3], h[0], w[3], h[3])
    434                );
    435            }
    436
    437            if (this.centerBlock != null) {
    438                this.centerBlock.setBounds(
    439                    new Rectangle2D.Double(
    440                        w[2], h[0], width - w[2] - w[3], height - h[0] - h[1]
    441                    )
    442                );
    443            }
    444            return new Size2D(width, height);
    445        }
    446
    447        /**
    448         * Arranges the items within a container.
    449         *
    450         * @param container  the container.
    451         * @param constraint  the constraint.
    452         * @param g2  the graphics device.
    453         *
    454         * @return The container size after the arrangement.
    455         */
    456        protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
    457                                   RectangleConstraint constraint) {
    458            double[] w = new double[5];
    459            double[] h = new double[5];
    460            w[0] = constraint.getWidth();
    461            if (this.topBlock != null) {
    462                RectangleConstraint c1 = new RectangleConstraint(
    463                    w[0], null, LengthConstraintType.FIXED,
    464                    0.0, new Range(0.0, constraint.getHeight()),
    465                    LengthConstraintType.RANGE
    466                );
    467                Size2D size = this.topBlock.arrange(g2, c1);
    468                h[0] = size.height;
    469            }
    470            w[1] = w[0];
    471            if (this.bottomBlock != null) {
    472                RectangleConstraint c2 = new RectangleConstraint(
    473                    w[0], null, LengthConstraintType.FIXED,
    474                    0.0, new Range(0.0, constraint.getHeight() - h[0]),
    475                    LengthConstraintType.RANGE
    476                );
    477                Size2D size = this.bottomBlock.arrange(g2, c2);
    478                h[1] = size.height;
    479            }
    480            h[2] = constraint.getHeight() - h[1] - h[0];
    481            if (this.leftBlock != null) {
    482                RectangleConstraint c3 = new RectangleConstraint(
    483                    0.0, new Range(0.0, constraint.getWidth()),
    484                    LengthConstraintType.RANGE,
    485                    h[2], null, LengthConstraintType.FIXED
    486                );
    487                Size2D size = this.leftBlock.arrange(g2, c3);
    488                w[2] = size.width;
    489            }
    490            h[3] = h[2];
    491            if (this.rightBlock != null) {
    492                RectangleConstraint c4 = new RectangleConstraint(
    493                    0.0, new Range(0.0, constraint.getWidth() - w[2]),
    494                    LengthConstraintType.RANGE,
    495                    h[2], null, LengthConstraintType.FIXED
    496                );
    497                Size2D size = this.rightBlock.arrange(g2, c4);
    498                w[3] = size.width;
    499            }
    500            h[4] = h[2];
    501            w[4] = constraint.getWidth() - w[3] - w[2];
    502            RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]);
    503            if (this.centerBlock != null) {
    504                this.centerBlock.arrange(g2, c5);
    505            }
    506
    507            if (this.topBlock != null) {
    508                this.topBlock.setBounds(
    509                    new Rectangle2D.Double(0.0, 0.0, w[0], h[0])
    510                );
    511            }
    512            if (this.bottomBlock != null) {
    513                this.bottomBlock.setBounds(
    514                    new Rectangle2D.Double(0.0, h[0] + h[2], w[1], h[1])
    515                );
    516            }
    517            if (this.leftBlock != null) {
    518                this.leftBlock.setBounds(
    519                    new Rectangle2D.Double(0.0, h[0], w[2], h[2])
    520                );
    521            }
    522            if (this.rightBlock != null) {
    523                this.rightBlock.setBounds(
    524                    new Rectangle2D.Double(w[2] + w[4], h[0], w[3], h[3])
    525                );
    526            }
    527            if (this.centerBlock != null) {
    528                this.centerBlock.setBounds(
    529                    new Rectangle2D.Double(w[2], h[0], w[4], h[4])
    530                );
    531            }
    532            return new Size2D(constraint.getWidth(), constraint.getHeight());
    533        }
    534
    535        /**
    536         * Clears the layout.
    537         */
    538        public void clear() {
    539            this.centerBlock = null;
    540            this.topBlock = null;
    541            this.bottomBlock = null;
    542            this.leftBlock = null;
    543            this.rightBlock = null;
    544        }
    545
    546        /**
    547         * Tests this arrangement for equality with an arbitrary object.
    548         *
    549         * @param obj  the object (<code>null</code> permitted).
    550         *
    551         * @return A boolean.
    552         */
    553        public boolean equals(Object obj) {
    554            if (obj == this) {
    555                return true;
    556            }
    557            if (!(obj instanceof BorderArrangement)) {
    558                return false;
    559            }
    560            BorderArrangement that = (BorderArrangement) obj;
    561            if (!ObjectUtilities.equal(this.topBlock, that.topBlock)) {
    562                return false;
    563            }
    564            if (!ObjectUtilities.equal(this.bottomBlock, that.bottomBlock)) {
    565                return false;
    566            }
    567            if (!ObjectUtilities.equal(this.leftBlock, that.leftBlock)) {
    568                return false;
    569            }
    570            if (!ObjectUtilities.equal(this.rightBlock, that.rightBlock)) {
    571                return false;
    572            }
    573            if (!ObjectUtilities.equal(this.centerBlock, that.centerBlock)) {
    574                return false;
    575            }
    576            return true;
    577        }
    578    }