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 * GridArrangement.java
029 * --------------------
030 * (C) Copyright 2005, 2007, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes:
036 * --------
037 * 08-Feb-2005 : Version 1 (DG);
038 * 
039 */
040
041package org.jfree.chart.block;
042
043import java.awt.Graphics2D;
044import java.awt.geom.Rectangle2D;
045import java.io.Serializable;
046import java.util.Iterator;
047import java.util.List;
048
049import org.jfree.ui.Size2D;
050
051/**
052 * Arranges blocks in a grid within their container.
053 */
054public class GridArrangement implements Arrangement, Serializable {
055    
056    /** For serialization. */
057    private static final long serialVersionUID = -2563758090144655938L;
058    
059    /** The rows. */
060    private int rows;
061    
062    /** The columns. */
063    private int columns;
064    
065    /**
066     * Creates a new grid arrangement.
067     * 
068     * @param rows  the row count.
069     * @param columns  the column count.
070     */
071    public GridArrangement(int rows, int columns) {
072        this.rows = rows;
073        this.columns = columns;
074    }
075    
076    /**
077     * Adds a block and a key which can be used to determine the position of 
078     * the block in the arrangement.  This method is called by the container 
079     * (you don't need to call this method directly) and gives the arrangement
080     * an opportunity to record the details if they are required.
081     * 
082     * @param block  the block.
083     * @param key  the key (<code>null</code> permitted).
084     */
085    public void add(Block block, Object key) {
086        // can safely ignore   
087    }
088    
089    /**
090     * Arranges the blocks within the specified container, subject to the given
091     * constraint.
092     * 
093     * @param container  the container.
094     * @param constraint  the constraint.
095     * @param g2  the graphics device.
096     * 
097     * @return The size following the arrangement.
098     */
099    public Size2D arrange(BlockContainer container, Graphics2D g2,
100                          RectangleConstraint constraint) {
101        LengthConstraintType w = constraint.getWidthConstraintType();
102        LengthConstraintType h = constraint.getHeightConstraintType();
103        if (w == LengthConstraintType.NONE) {
104            if (h == LengthConstraintType.NONE) {
105                return arrangeNN(container, g2);  
106            }
107            else if (h == LengthConstraintType.FIXED) {
108                
109                throw new RuntimeException("Not yet implemented.");  
110            }
111            else if (h == LengthConstraintType.RANGE) {
112                // find optimum height, then map to range
113                throw new RuntimeException("Not yet implemented.");  
114            }
115        }
116        else if (w == LengthConstraintType.FIXED) {
117            if (h == LengthConstraintType.NONE) {
118                // find optimum height
119                return arrangeFN(container, g2, constraint);  
120            }
121            else if (h == LengthConstraintType.FIXED) {
122                return arrangeFF(container, g2, constraint);
123            }
124            else if (h == LengthConstraintType.RANGE) {
125                // find optimum height and map to range
126                return arrangeFR(container, g2, constraint);  
127            }
128        }
129        else if (w == LengthConstraintType.RANGE) {
130            // find optimum width and map to range
131            if (h == LengthConstraintType.NONE) {
132                // find optimum height
133                throw new RuntimeException("Not yet implemented.");  
134            }
135            else if (h == LengthConstraintType.FIXED) {
136                // fixed width
137                throw new RuntimeException("Not yet implemented.");  
138            }
139            else if (h == LengthConstraintType.RANGE) {
140                throw new RuntimeException("Not yet implemented.");  
141            }
142        }
143        return new Size2D();  // TODO: complete this
144    }
145    
146    /**
147     * Arranges the container with no constraint on the width or height.
148     * 
149     * @param container  the container.
150     * @param g2  the graphics device.
151     * 
152     * @return The size.
153     */
154    protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
155        double maxW = 0.0;
156        double maxH = 0.0;
157        List blocks = container.getBlocks();
158        Iterator iterator = blocks.iterator();
159        while (iterator.hasNext()) {
160            Block b = (Block) iterator.next();
161            Size2D s = b.arrange(g2, RectangleConstraint.NONE);
162            maxW = Math.max(maxW, s.width);
163            maxH = Math.max(maxH, s.height);
164        }
165        double width = this.columns * maxW;
166        double height = this.rows * maxH;
167        RectangleConstraint c = new RectangleConstraint(width, height);
168        return arrangeFF(container, g2, c);
169    }
170    
171    /**
172     * Arranges the container with a fixed overall width and height.
173     * 
174     * @param container  the container.
175     * @param g2  the graphics device.
176     * @param constraint  the constraint.
177     * 
178     * @return The size following the arrangement.
179     */
180    protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
181                               RectangleConstraint constraint) {
182        double width = constraint.getWidth() /  this.columns;
183        double height = constraint.getHeight() / this.rows;
184        List blocks = container.getBlocks();
185        for (int c = 0; c < this.columns; c++) {
186            for (int r = 0; r < this.rows; r++) {
187                int index = r * this.columns + c;
188                if (index == blocks.size()) {
189                    break;   
190                }
191                Block b = (Block) blocks.get(index);
192                b.setBounds(new Rectangle2D.Double(
193                    c * width, r * height, width, height
194                ));
195            }
196        }
197        return new Size2D(this.columns * width, this.rows * height);
198    }
199
200    /**
201     * Arrange with a fixed width and a height within a given range.
202     * 
203     * @param container  the container.
204     * @param constraint  the constraint.
205     * @param g2  the graphics device.
206     * 
207     * @return The size of the arrangement.
208     */
209    protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
210                               RectangleConstraint constraint) {
211        
212        RectangleConstraint c1 = constraint.toUnconstrainedHeight();
213        Size2D size1 = arrange(container, g2, c1);
214
215        if (constraint.getHeightRange().contains(size1.getHeight())) {
216            return size1;   
217        }
218        else {
219            double h = constraint.getHeightRange().constrain(size1.getHeight());
220            RectangleConstraint c2 = constraint.toFixedHeight(h);
221            return arrange(container, g2, c2);
222        }
223    }
224
225    /**
226     * Arrange with a fixed width and a height within a given range.
227     * 
228     * @param container  the container.
229     * @param g2  the graphics device.
230     * @param constraint  the constraint.
231     * 
232     * @return The size of the arrangement.
233     */
234    protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
235                               RectangleConstraint constraint) {
236        
237        double width = constraint.getWidth() /  this.columns;
238        RectangleConstraint constraint2 = constraint.toFixedWidth(width);
239        List blocks = container.getBlocks();
240        double maxH = 0.0;
241        for (int r = 0; r < this.rows; r++) {
242            for (int c = 0; c < this.columns; c++) {
243                int index = r * this.columns + c;
244                if (index == blocks.size()) {
245                    break;   
246                }
247                Block b = (Block) blocks.get(index);
248                Size2D s = b.arrange(g2, constraint2);
249                maxH = Math.max(maxH, s.getHeight());
250            }
251        }
252        RectangleConstraint constraint3 = constraint.toFixedHeight(
253            maxH * this.rows
254        );
255        return arrange(container, g2, constraint3);
256    }
257
258    /**
259     * Clears any cached layout information retained by the arrangement.
260     */
261    public void clear() {
262        // nothing to clear   
263    }
264    
265    /**
266     * Compares this layout manager for equality with an arbitrary object.
267     * 
268     * @param obj  the object.
269     * 
270     * @return A boolean.
271     */
272    public boolean equals(Object obj) {
273        if (obj == this) {
274            return true;
275        }
276        if (!(obj instanceof GridArrangement)) {
277            return false;   
278        }
279        GridArrangement that = (GridArrangement) obj;
280        if (this.columns != that.columns) {
281            return false;   
282        }
283        if (this.rows != that.rows) {
284            return false;   
285        }
286        return true;
287    }
288
289}