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