    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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-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
    042    package org.jfree.chart.block;
    043
    044    import java.awt.Graphics2D;
    045    import java.awt.geom.Rectangle2D;
    046    import java.io.Serializable;
    047    import java.util.ArrayList;
    048    import java.util.List;
    049
    050    import org.jfree.ui.HorizontalAlignment;
    051    import org.jfree.ui.Size2D;
    052    import org.jfree.ui.VerticalAlignment;
    053
    054    /**
    055     * Arranges blocks in a column layout.  This class is immutable.
    056     */
    057    public 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    }