001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * CenterArrangement.java
029 * ----------------------
030 * (C) Copyright 2005-present, by David Gilbert.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.chart.block;
038
039import java.awt.Graphics2D;
040import java.awt.geom.Rectangle2D;
041import java.io.Serializable;
042import java.util.List;
043import org.jfree.chart.ui.Size2D;
044
045/**
046 * Arranges a block in the center of its container.  This class is immutable.
047 */
048public class CenterArrangement implements Arrangement, Serializable {
049
050    /** For serialization. */
051    private static final long serialVersionUID = -353308149220382047L;
052
053    /**
054     * Creates a new instance.
055     */
056    public CenterArrangement() {
057    }
058
059    /**
060     * Adds a block to be managed by this instance.  This method is usually
061     * called by the {@link BlockContainer}, you shouldn't need to call it
062     * directly.
063     *
064     * @param block  the block.
065     * @param key  a key that controls the position of the block.
066     */
067    @Override
068    public void add(Block block, Object key) {
069        // since the flow layout is relatively straightforward,
070        // no information needs to be recorded here
071    }
072
073    /**
074     * Calculates and sets the bounds of all the items in the specified
075     * container, subject to the given constraint.  The {@code Graphics2D}
076     * can be used by some items (particularly items containing text) to
077     * calculate sizing parameters.
078     *
079     * @param container  the container whose items are being arranged.
080     * @param g2  the graphics device.
081     * @param constraint  the size constraint.
082     *
083     * @return The size of the container after arrangement of the contents.
084     */
085    @Override
086    public Size2D arrange(BlockContainer container, Graphics2D g2,
087                          RectangleConstraint constraint) {
088
089        LengthConstraintType w = constraint.getWidthConstraintType();
090        LengthConstraintType h = constraint.getHeightConstraintType();
091        if (w == LengthConstraintType.NONE) {
092            if (h == LengthConstraintType.NONE) {
093                return arrangeNN(container, g2);
094            }
095            else if (h == LengthConstraintType.FIXED) {
096                throw new RuntimeException("Not implemented.");
097            }
098            else if (h == LengthConstraintType.RANGE) {
099                throw new RuntimeException("Not implemented.");
100            }
101        }
102        else if (w == LengthConstraintType.FIXED) {
103            if (h == LengthConstraintType.NONE) {
104                return arrangeFN(container, g2, constraint);
105            }
106            else if (h == LengthConstraintType.FIXED) {
107                throw new RuntimeException("Not implemented.");
108            }
109            else if (h == LengthConstraintType.RANGE) {
110                throw new RuntimeException("Not implemented.");
111            }
112        }
113        else if (w == LengthConstraintType.RANGE) {
114            if (h == LengthConstraintType.NONE) {
115                return arrangeRN(container, g2, constraint);
116            }
117            else if (h == LengthConstraintType.FIXED) {
118                return arrangeRF(container, g2, constraint);
119            }
120            else if (h == LengthConstraintType.RANGE) {
121                return arrangeRR(container, g2, constraint);
122            }
123        }
124        throw new IllegalArgumentException("Unknown LengthConstraintType.");
125
126    }
127
128    /**
129     * Arranges the blocks in the container with a fixed width and no height
130     * constraint.
131     *
132     * @param container  the container.
133     * @param g2  the graphics device.
134     * @param constraint  the constraint.
135     *
136     * @return The size.
137     */
138    protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
139                               RectangleConstraint constraint) {
140
141        List blocks = container.getBlocks();
142        Block b = (Block) blocks.get(0);
143        Size2D s = b.arrange(g2, RectangleConstraint.NONE);
144        double width = constraint.getWidth();
145        Rectangle2D bounds = new Rectangle2D.Double((width - s.width) / 2.0,
146                0.0, s.width, s.height);
147        b.setBounds(bounds);
148        return new Size2D((width - s.width) / 2.0, s.height);
149    }
150
151    /**
152     * Arranges the blocks in the container with a fixed with and a range
153     * constraint on the height.
154     *
155     * @param container  the container.
156     * @param g2  the graphics device.
157     * @param constraint  the constraint.
158     *
159     * @return The size following the arrangement.
160     */
161    protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
162                               RectangleConstraint constraint) {
163
164        Size2D s = arrangeFN(container, g2, constraint);
165        if (constraint.getHeightRange().contains(s.height)) {
166            return s;
167        }
168        else {
169            RectangleConstraint c = constraint.toFixedHeight(
170                    constraint.getHeightRange().constrain(s.getHeight()));
171            return arrangeFF(container, g2, c);
172        }
173    }
174
175    /**
176     * Arranges the blocks in the container with the overall height and width
177     * specified as fixed constraints.
178     *
179     * @param container  the container.
180     * @param g2  the graphics device.
181     * @param constraint  the constraint.
182     *
183     * @return The size following the arrangement.
184     */
185    protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
186                               RectangleConstraint constraint) {
187
188        // TODO: implement this properly
189        return arrangeFN(container, g2, constraint);
190    }
191
192    /**
193     * Arranges the blocks with the overall width and height to fit within
194     * specified ranges.
195     *
196     * @param container  the container.
197     * @param g2  the graphics device.
198     * @param constraint  the constraint.
199     *
200     * @return The size after the arrangement.
201     */
202    protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
203                               RectangleConstraint constraint) {
204
205        // first arrange without constraints, and see if this fits within
206        // the required ranges...
207        Size2D s1 = arrangeNN(container, g2);
208        if (constraint.getWidthRange().contains(s1.width)) {
209            return s1;  // TODO: we didn't check the height yet
210        }
211        else {
212            RectangleConstraint c = constraint.toFixedWidth(
213                    constraint.getWidthRange().getUpperBound());
214            return arrangeFR(container, g2, c);
215        }
216    }
217
218    /**
219     * Arranges the blocks in the container with a range constraint on the
220     * width and a fixed height.
221     *
222     * @param container  the container.
223     * @param g2  the graphics device.
224     * @param constraint  the constraint.
225     *
226     * @return The size following the arrangement.
227     */
228    protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
229                               RectangleConstraint constraint) {
230
231        Size2D s = arrangeNF(container, g2, constraint);
232        if (constraint.getWidthRange().contains(s.width)) {
233            return s;
234        }
235        else {
236            RectangleConstraint c = constraint.toFixedWidth(
237                    constraint.getWidthRange().constrain(s.getWidth()));
238            return arrangeFF(container, g2, c);
239        }
240    }
241
242    /**
243     * Arranges the block with a range constraint on the width, and no
244     * constraint on the height.
245     *
246     * @param container  the container.
247     * @param g2  the graphics device.
248     * @param constraint  the constraint.
249     *
250     * @return The size following the arrangement.
251     */
252    protected Size2D arrangeRN(BlockContainer container, Graphics2D g2,
253                               RectangleConstraint constraint) {
254        // first arrange without constraints, then see if the width fits
255        // within the required range...if not, call arrangeFN() at max width
256        Size2D s1 = arrangeNN(container, g2);
257        if (constraint.getWidthRange().contains(s1.width)) {
258            return s1;
259        }
260        else {
261            RectangleConstraint c = constraint.toFixedWidth(
262                    constraint.getWidthRange().getUpperBound());
263            return arrangeFN(container, g2, c);
264        }
265    }
266
267    /**
268     * Arranges the blocks without any constraints.  This puts all blocks
269     * into a single row.
270     *
271     * @param container  the container.
272     * @param g2  the graphics device.
273     *
274     * @return The size after the arrangement.
275     */
276    protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
277        List blocks = container.getBlocks();
278        Block b = (Block) blocks.get(0);
279        Size2D s = b.arrange(g2, RectangleConstraint.NONE);
280        b.setBounds(new Rectangle2D.Double(0.0, 0.0, s.width, s.height));
281        return new Size2D(s.width, s.height);
282    }
283
284    /**
285     * Arranges the blocks with no width constraint and a fixed height
286     * constraint.  This puts all blocks into a single row.
287     *
288     * @param container  the container.
289     * @param g2  the graphics device.
290     * @param constraint  the constraint.
291     *
292     * @return The size after the arrangement.
293     */
294    protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
295                               RectangleConstraint constraint) {
296        // TODO: for now we are ignoring the height constraint
297        return arrangeNN(container, g2);
298    }
299
300    /**
301     * Clears any cached information.
302     */
303    @Override
304    public void clear() {
305        // no action required.
306    }
307
308    /**
309     * Tests this instance for equality with an arbitrary object.
310     *
311     * @param obj  the object ({@code null} permitted).
312     *
313     * @return A boolean.
314     */
315    @Override
316    public boolean equals(Object obj) {
317        if (obj == this) {
318            return true;
319        }
320        if (!(obj instanceof CenterArrangement)) {
321            return false;
322        }
323        return true;
324    }
325
326}