    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 * 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
    047package org.jfree.chart.block;
    048
    049import java.awt.Graphics2D;
    050import java.awt.geom.Rectangle2D;
    051import java.io.Serializable;
    052
    053import org.jfree.data.Range;
    054import org.jfree.ui.RectangleEdge;
    055import org.jfree.ui.Size2D;
    056import 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 */
    062public 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}