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    
041    package org.jfree.chart.block;
042    
043    import java.awt.Graphics2D;
044    import java.awt.geom.Rectangle2D;
045    import java.io.Serializable;
046    import java.util.Iterator;
047    import java.util.List;
048    
049    import org.jfree.ui.Size2D;
050    
051    /**
052     * Arranges blocks in a grid within their container.
053     */
054    public 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    }