    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2007, 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-2007, 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        protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
    250                                   RectangleConstraint constraint) {
    251
    252            // first arrange without constraints, and see if this fits within
    253            // the required ranges...
    254            Size2D s1 = arrangeNN(container, g2);
    255            if (constraint.getHeightRange().contains(s1.height)) {
    256                return s1;  // TODO: we didn't check the width yet
    257            }
    258            else {
    259                RectangleConstraint c = constraint.toFixedHeight(
    260                    constraint.getHeightRange().getUpperBound()
    261                );
    262                return arrangeRF(container, g2, c);
    263            }
    264        }
    265
    266        /**
    267         * Arranges the blocks in the container using a fixed height and a
    268         * range for the width.
    269         *
    270         * @param container  the container.
    271         * @param g2  the graphics device.
    272         * @param constraint  the constraint.
    273         *
    274         * @return The size of the container after arrangement.
    275         */
    276        protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
    277                                   RectangleConstraint constraint) {
    278
    279            Size2D s = arrangeNF(container, g2, constraint);
    280            if (constraint.getWidthRange().contains(s.width)) {
    281                return s;
    282            }
    283            else {
    284                RectangleConstraint c = constraint.toFixedWidth(
    285                    constraint.getWidthRange().constrain(s.getWidth())
    286                );
    287                return arrangeFF(container, g2, c);
    288            }
    289        }
    290
    291        /**
    292         * Arranges the blocks without any constraints.  This puts all blocks
    293         * into a single column.
    294         *
    295         * @param container  the container.
    296         * @param g2  the graphics device.
    297         *
    298         * @return The size after the arrangement.
    299         */
    300        protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
    301            double y = 0.0;
    302            double height = 0.0;
    303            double maxWidth = 0.0;
    304            List blocks = container.getBlocks();
    305            int blockCount = blocks.size();
    306            if (blockCount > 0) {
    307                Size2D[] sizes = new Size2D[blocks.size()];
    308                for (int i = 0; i < blocks.size(); i++) {
    309                    Block block = (Block) blocks.get(i);
    310                    sizes[i] = block.arrange(g2, RectangleConstraint.NONE);
    311                    height = height + sizes[i].getHeight();
    312                    maxWidth = Math.max(sizes[i].width, maxWidth);
    313                    block.setBounds(
    314                        new Rectangle2D.Double(
    315                            0.0, y, sizes[i].width, sizes[i].height
    316                        )
    317                    );
    318                    y = y + sizes[i].height + this.verticalGap;
    319                }
    320                if (blockCount > 1) {
    321                    height = height + this.verticalGap * (blockCount - 1);
    322                }
    323                if (this.horizontalAlignment != HorizontalAlignment.LEFT) {
    324                    for (int i = 0; i < blocks.size(); i++) {
    325                        //Block b = (Block) blocks.get(i);
    326                        if (this.horizontalAlignment
    327                                == HorizontalAlignment.CENTER) {
    328                            //TODO: shift block right by half
    329                        }
    330                        else if (this.horizontalAlignment
    331                                == HorizontalAlignment.RIGHT) {
    332                            //TODO: shift block over to right
    333                        }
    334                    }
    335                }
    336            }
    337            return new Size2D(maxWidth, height);
    338        }
    339
    340        /**
    341         * Clears any cached information.
    342         */
    343        public void clear() {
    344            // no action required.
    345        }
    346
    347        /**
    348         * Tests this instance for equality with an arbitrary object.
    349         *
    350         * @param obj  the object (<code>null</code> permitted).
    351         *
    352         * @return A boolean.
    353         */
    354        public boolean equals(Object obj) {
    355            if (obj == this) {
    356                return true;
    357            }
    358            if (!(obj instanceof ColumnArrangement)) {
    359                return false;
    360            }
    361            ColumnArrangement that = (ColumnArrangement) obj;
    362            if (this.horizontalAlignment != that.horizontalAlignment) {
    363                return false;
    364            }
    365            if (this.verticalAlignment != that.verticalAlignment) {
    366                return false;
    367            }
    368            if (this.horizontalGap != that.horizontalGap) {
    369                return false;
    370            }
    371            if (this.verticalGap != that.verticalGap) {
    372                return false;
    373            }
    374            return true;
    375        }
    376
    377
    378    }