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