    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 * ColumnArrangement.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 * 04-Feb-2005 : Added equals() and implemented Serializable (DG);
    039 *
    040 */
    041
    042package org.jfree.chart.block;
    043
    044import java.awt.Graphics2D;
    045import java.awt.geom.Rectangle2D;
    046import java.io.Serializable;
    047import java.util.ArrayList;
    048import java.util.List;
    049
    050import org.jfree.ui.HorizontalAlignment;
    051import org.jfree.ui.Size2D;
    052import org.jfree.ui.VerticalAlignment;
    053
    054/**
    055 * Arranges blocks in a column layout.  This class is immutable.
    056 */
    057public class ColumnArrangement implements Arrangement, Serializable {
    058
    059    /** For serialization. */
    060    private static final long serialVersionUID = -5315388482898581555L;
    061
    062    /** The horizontal alignment of blocks. */
    063    private HorizontalAlignment horizontalAlignment;
    064
    065    /** The vertical alignment of blocks within each row. */
    066    private VerticalAlignment verticalAlignment;
    067
    068    /** The horizontal gap between columns. */
    069    private double horizontalGap;
    070
    071    /** The vertical gap between items in a column. */
    072    private double verticalGap;
    073
    074    /**
    075     * Creates a new instance.
    076     */
    077    public ColumnArrangement() {
    078    }
    079
    080    /**
    081     * Creates a new instance.
    082     *
    083     * @param hAlign  the horizontal alignment (currently ignored).
    084     * @param vAlign  the vertical alignment (currently ignored).
    085     * @param hGap  the horizontal gap.
    086     * @param vGap  the vertical gap.
    087     */
    088    public ColumnArrangement(HorizontalAlignment hAlign,
    089                             VerticalAlignment vAlign,
    090                             double hGap, double vGap) {
    091        this.horizontalAlignment = hAlign;
    092        this.verticalAlignment = vAlign;
    093        this.horizontalGap = hGap;
    094        this.verticalGap = vGap;
    095    }
    096
    097    /**
    098     * Adds a block to be managed by this instance.  This method is usually
    099     * called by the {@link BlockContainer}, you shouldn't need to call it
    100     * directly.
    101     *
    102     * @param block  the block.
    103     * @param key  a key that controls the position of the block.
    104     */
    105    public void add(Block block, Object key) {
    106        // since the flow layout is relatively straightforward, no information
    107        // needs to be recorded here
    108    }
    109
    110    /**
    111     * Calculates and sets the bounds of all the items in the specified
    112     * container, subject to the given constraint.  The <code>Graphics2D</code>
    113     * can be used by some items (particularly items containing text) to
    114     * calculate sizing parameters.
    115     *
    116     * @param container  the container whose items are being arranged.
    117     * @param g2  the graphics device.
    118     * @param constraint  the size constraint.
    119     *
    120     * @return The size of the container after arrangement of the contents.
    121     */
    122    public Size2D arrange(BlockContainer container, Graphics2D g2,
    123                          RectangleConstraint constraint) {
    124
    125        LengthConstraintType w = constraint.getWidthConstraintType();
    126        LengthConstraintType h = constraint.getHeightConstraintType();
    127        if (w == LengthConstraintType.NONE) {
    128            if (h == LengthConstraintType.NONE) {
    129                return arrangeNN(container, g2);
    130            }
    131            else if (h == LengthConstraintType.FIXED) {
    132                throw new RuntimeException("Not implemented.");
    133            }
    134            else if (h == LengthConstraintType.RANGE) {
    135                throw new RuntimeException("Not implemented.");
    136            }
    137        }
    138        else if (w == LengthConstraintType.FIXED) {
    139            if (h == LengthConstraintType.NONE) {
    140                throw new RuntimeException("Not implemented.");
    141            }
    142            else if (h == LengthConstraintType.FIXED) {
    143                return arrangeFF(container, g2, constraint);
    144            }
    145            else if (h == LengthConstraintType.RANGE) {
    146                throw new RuntimeException("Not implemented.");
    147            }
    148        }
    149        else if (w == LengthConstraintType.RANGE) {
    150            if (h == LengthConstraintType.NONE) {
    151                throw new RuntimeException("Not implemented.");
    152            }
    153            else if (h == LengthConstraintType.FIXED) {
    154                return arrangeRF(container, g2, constraint);
    155            }
    156            else if (h == LengthConstraintType.RANGE) {
    157                return arrangeRR(container, g2, constraint);
    158            }
    159        }
    160        return new Size2D();  // TODO: complete this
    161
    162    }
    163
    164    /**
    165     * Calculates and sets the bounds of all the items in the specified
    166     * container, subject to the given constraint.  The <code>Graphics2D</code>
    167     * can be used by some items (particularly items containing text) to
    168     * calculate sizing parameters.
    169     *
    170     * @param container  the container whose items are being arranged.
    171     * @param g2  the graphics device.
    172     * @param constraint  the size constraint.
    173     *
    174     * @return The container size after the arrangement.
    175     */
    176    protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
    177                               RectangleConstraint constraint) {
    178        // TODO: implement properly
    179        return arrangeNF(container, g2, constraint);
    180    }
    181
    182    /**
    183     * Calculates and sets the bounds of all the items in the specified
    184     * container, subject to the given constraint.  The <code>Graphics2D</code>
    185     * can be used by some items (particularly items containing text) to
    186     * calculate sizing parameters.
    187     *
    188     * @param container  the container whose items are being arranged.
    189     * @param constraint  the size constraint.
    190     * @param g2  the graphics device.
    191     *
    192     * @return The container size after the arrangement.
    193     */
    194    protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
    195                               RectangleConstraint constraint) {
    196
    197        List blocks = container.getBlocks();
    198
    199        double height = constraint.getHeight();
    200        if (height <= 0.0) {
    201            height = Double.POSITIVE_INFINITY;
    202        }
    203
    204        double x = 0.0;
    205        double y = 0.0;
    206        double maxWidth = 0.0;
    207        List itemsInColumn = new ArrayList();
    208        for (int i = 0; i < blocks.size(); i++) {
    209            Block block = (Block) blocks.get(i);
    210            Size2D size = block.arrange(g2, RectangleConstraint.NONE);
    211            if (y + size.height <= height) {
    212                itemsInColumn.add(block);
    213                block.setBounds(
    214                    new Rectangle2D.Double(x, y, size.width, size.height)
    215                );
    216                y = y + size.height + this.verticalGap;
    217                maxWidth = Math.max(maxWidth, size.width);
    218            }
    219            else {
    220                if (itemsInColumn.isEmpty()) {
    221                    // place in this column (truncated) anyway
    222                    block.setBounds(
    223                        new Rectangle2D.Double(
    224                            x, y, size.width, Math.min(size.height, height - y)
    225                        )
    226                    );
    227                    y = 0.0;
    228                    x = x + size.width + this.horizontalGap;
    229                }
    230                else {
    231                    // start new column
    232                    itemsInColumn.clear();
    233                    x = x + maxWidth + this.horizontalGap;
    234                    y = 0.0;
    235                    maxWidth = size.width;
    236                    block.setBounds(
    237                        new Rectangle2D.Double(
    238                            x, y, size.width, Math.min(size.height, height)
    239                        )
    240                    );
    241                    y = size.height + this.verticalGap;
    242                    itemsInColumn.add(block);
    243                }
    244            }
    245        }
    246        return new Size2D(x + maxWidth, constraint.getHeight());
    247    }
    248
    249    /**
    250     * Arranges a container with range constraints for both the horizontal
    251     * and vertical.
    252     *
    253     * @param container  the container.
    254     * @param g2  the graphics device.
    255     * @param constraint  the constraint.
    256     *
    257     * @return The size of the container.
    258     */
    259    protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
    260                               RectangleConstraint constraint) {
    261
    262        // first arrange without constraints, and see if this fits within
    263        // the required ranges...
    264        Size2D s1 = arrangeNN(container, g2);
    265        if (constraint.getHeightRange().contains(s1.height)) {
    266            return s1;  // TODO: we didn't check the width yet
    267        }
    268        else {
    269            RectangleConstraint c = constraint.toFixedHeight(
    270                constraint.getHeightRange().getUpperBound()
    271            );
    272            return arrangeRF(container, g2, c);
    273        }
    274    }
    275
    276    /**
    277     * Arranges the blocks in the container using a fixed height and a
    278     * range for the width.
    279     *
    280     * @param container  the container.
    281     * @param g2  the graphics device.
    282     * @param constraint  the constraint.
    283     *
    284     * @return The size of the container after arrangement.
    285     */
    286    protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
    287                               RectangleConstraint constraint) {
    288
    289        Size2D s = arrangeNF(container, g2, constraint);
    290        if (constraint.getWidthRange().contains(s.width)) {
    291            return s;
    292        }
    293        else {
    294            RectangleConstraint c = constraint.toFixedWidth(
    295                constraint.getWidthRange().constrain(s.getWidth())
    296            );
    297            return arrangeFF(container, g2, c);
    298        }
    299    }
    300
    301    /**
    302     * Arranges the blocks without any constraints.  This puts all blocks
    303     * into a single column.
    304     *
    305     * @param container  the container.
    306     * @param g2  the graphics device.
    307     *
    308     * @return The size after the arrangement.
    309     */
    310    protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
    311        double y = 0.0;
    312        double height = 0.0;
    313        double maxWidth = 0.0;
    314        List blocks = container.getBlocks();
    315        int blockCount = blocks.size();
    316        if (blockCount > 0) {
    317            Size2D[] sizes = new Size2D[blocks.size()];
    318            for (int i = 0; i < blocks.size(); i++) {
    319                Block block = (Block) blocks.get(i);
    320                sizes[i] = block.arrange(g2, RectangleConstraint.NONE);
    321                height = height + sizes[i].getHeight();
    322                maxWidth = Math.max(sizes[i].width, maxWidth);
    323                block.setBounds(
    324                    new Rectangle2D.Double(
    325                        0.0, y, sizes[i].width, sizes[i].height
    326                    )
    327                );
    328                y = y + sizes[i].height + this.verticalGap;
    329            }
    330            if (blockCount > 1) {
    331                height = height + this.verticalGap * (blockCount - 1);
    332            }
    333            if (this.horizontalAlignment != HorizontalAlignment.LEFT) {
    334                for (int i = 0; i < blocks.size(); i++) {
    335                    //Block b = (Block) blocks.get(i);
    336                    if (this.horizontalAlignment
    337                            == HorizontalAlignment.CENTER) {
    338                        //TODO: shift block right by half
    339                    }
    340                    else if (this.horizontalAlignment
    341                            == HorizontalAlignment.RIGHT) {
    342                        //TODO: shift block over to right
    343                    }
    344                }
    345            }
    346        }
    347        return new Size2D(maxWidth, height);
    348    }
    349
    350    /**
    351     * Clears any cached information.
    352     */
    353    public void clear() {
    354        // no action required.
    355    }
    356
    357    /**
    358     * Tests this instance for equality with an arbitrary object.
    359     *
    360     * @param obj  the object (<code>null</code> permitted).
    361     *
    362     * @return A boolean.
    363     */
    364    public boolean equals(Object obj) {
    365        if (obj == this) {
    366            return true;
    367        }
    368        if (!(obj instanceof ColumnArrangement)) {
    369            return false;
    370        }
    371        ColumnArrangement that = (ColumnArrangement) obj;
    372        if (this.horizontalAlignment != that.horizontalAlignment) {
    373            return false;
    374        }
    375        if (this.verticalAlignment != that.verticalAlignment) {
    376            return false;
    377        }
    378        if (this.horizontalGap != that.horizontalGap) {
    379            return false;
    380        }
    381        if (this.verticalGap != that.verticalGap) {
    382            return false;
    383        }
    384        return true;
    385    }
    386
    387
    388}