001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, 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-2008, 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     * 03-Dec-2008 : Implemented missing methods, and fixed bugs reported in
039     *               patch 2370487 (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 (<code>null</code> not permitted).
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                    return arrangeNF(container, g2, constraint);
111                }
112                else if (h == LengthConstraintType.RANGE) {
113                    // find optimum height, then map to range
114                    return arrangeNR(container, g2, constraint);
115                }
116            }
117            else if (w == LengthConstraintType.FIXED) {
118                if (h == LengthConstraintType.NONE) {
119                    // find optimum height
120                    return arrangeFN(container, g2, constraint);
121                }
122                else if (h == LengthConstraintType.FIXED) {
123                    return arrangeFF(container, g2, constraint);
124                }
125                else if (h == LengthConstraintType.RANGE) {
126                    // find optimum height and map to range
127                    return arrangeFR(container, g2, constraint);
128                }
129            }
130            else if (w == LengthConstraintType.RANGE) {
131                // find optimum width and map to range
132                if (h == LengthConstraintType.NONE) {
133                    // find optimum height
134                    return arrangeRN(container, g2, constraint);
135                }
136                else if (h == LengthConstraintType.FIXED) {
137                    // fixed width
138                    return arrangeRF(container, g2, constraint);
139                }
140                else if (h == LengthConstraintType.RANGE) {
141                    return arrangeRR(container, g2, constraint);
142                }
143            }
144            throw new RuntimeException("Should never get to here!");
145        }
146    
147        /**
148         * Arranges the container with no constraint on the width or height.
149         *
150         * @param container  the container (<code>null</code> not permitted).
151         * @param g2  the graphics device.
152         *
153         * @return The size.
154         */
155        protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) {
156            double maxW = 0.0;
157            double maxH = 0.0;
158            List blocks = container.getBlocks();
159            Iterator iterator = blocks.iterator();
160            while (iterator.hasNext()) {
161                Block b = (Block) iterator.next();
162                if (b != null) {
163                    Size2D s = b.arrange(g2, RectangleConstraint.NONE);
164                    maxW = Math.max(maxW, s.width);
165                    maxH = Math.max(maxH, s.height);
166                }
167            }
168            double width = this.columns * maxW;
169            double height = this.rows * maxH;
170            RectangleConstraint c = new RectangleConstraint(width, height);
171            return arrangeFF(container, g2, c);
172        }
173    
174        /**
175         * Arranges the container with a fixed overall width and height.
176         *
177         * @param container  the container (<code>null</code> not permitted).
178         * @param g2  the graphics device.
179         * @param constraint  the constraint (<code>null</code> not permitted).
180         *
181         * @return The size following the arrangement.
182         */
183        protected Size2D arrangeFF(BlockContainer container, Graphics2D g2,
184                                   RectangleConstraint constraint) {
185            double width = constraint.getWidth() / this.columns;
186            double height = constraint.getHeight() / this.rows;
187            List blocks = container.getBlocks();
188            for (int c = 0; c < this.columns; c++) {
189                for (int r = 0; r < this.rows; r++) {
190                    int index = r * this.columns + c;
191                    if (index >= blocks.size()) {
192                        break;
193                    }
194                    Block b = (Block) blocks.get(index);
195                    if (b != null) {
196                        b.setBounds(new Rectangle2D.Double(c * width, r * height,
197                                width, height));
198                    }
199                }
200            }
201            return new Size2D(this.columns * width, this.rows * height);
202        }
203    
204        /**
205         * Arrange with a fixed width and a height within a given range.
206         *
207         * @param container  the container.
208         * @param constraint  the constraint.
209         * @param g2  the graphics device.
210         *
211         * @return The size of the arrangement.
212         */
213        protected Size2D arrangeFR(BlockContainer container, Graphics2D g2,
214                                   RectangleConstraint constraint) {
215    
216            RectangleConstraint c1 = constraint.toUnconstrainedHeight();
217            Size2D size1 = arrange(container, g2, c1);
218    
219            if (constraint.getHeightRange().contains(size1.getHeight())) {
220                return size1;
221            }
222            else {
223                double h = constraint.getHeightRange().constrain(size1.getHeight());
224                RectangleConstraint c2 = constraint.toFixedHeight(h);
225                return arrange(container, g2, c2);
226            }
227        }
228    
229        /**
230         * Arrange with a fixed height and a width within a given range.
231         *
232         * @param container  the container.
233         * @param constraint  the constraint.
234         * @param g2  the graphics device.
235         *
236         * @return The size of the arrangement.
237         */
238        protected Size2D arrangeRF(BlockContainer container, Graphics2D g2,
239                                   RectangleConstraint constraint) {
240    
241            RectangleConstraint c1 = constraint.toUnconstrainedWidth();
242            Size2D size1 = arrange(container, g2, c1);
243    
244            if (constraint.getWidthRange().contains(size1.getWidth())) {
245                return size1;
246            }
247            else {
248                double w = constraint.getWidthRange().constrain(size1.getWidth());
249                RectangleConstraint c2 = constraint.toFixedWidth(w);
250                return arrange(container, g2, c2);
251            }
252        }
253    
254        /**
255         * Arrange with a fixed width and no height constraint.
256         *
257         * @param container  the container.
258         * @param constraint  the constraint.
259         * @param g2  the graphics device.
260         *
261         * @return The size of the arrangement.
262         */
263        protected Size2D arrangeRN(BlockContainer container, Graphics2D g2,
264                                   RectangleConstraint constraint) {
265    
266            RectangleConstraint c1 = constraint.toUnconstrainedWidth();
267            Size2D size1 = arrange(container, g2, c1);
268    
269            if (constraint.getWidthRange().contains(size1.getWidth())) {
270                return size1;
271            }
272            else {
273                double w = constraint.getWidthRange().constrain(size1.getWidth());
274                RectangleConstraint c2 = constraint.toFixedWidth(w);
275                return arrange(container, g2, c2);
276            }
277        }
278    
279        /**
280         * Arrange with a fixed height and no width constraint.
281         *
282         * @param container  the container.
283         * @param constraint  the constraint.
284         * @param g2  the graphics device.
285         *
286         * @return The size of the arrangement.
287         */
288        protected Size2D arrangeNR(BlockContainer container, Graphics2D g2,
289                                   RectangleConstraint constraint) {
290    
291            RectangleConstraint c1 = constraint.toUnconstrainedHeight();
292            Size2D size1 = arrange(container, g2, c1);
293    
294            if (constraint.getHeightRange().contains(size1.getHeight())) {
295                return size1;
296            }
297            else {
298                double h = constraint.getHeightRange().constrain(size1.getHeight());
299                RectangleConstraint c2 = constraint.toFixedHeight(h);
300                return arrange(container, g2, c2);
301            }
302        }
303    
304        /**
305         * Arrange with ranges for both the width and height constraints.
306         *
307         * @param container  the container.
308         * @param constraint  the constraint.
309         * @param g2  the graphics device.
310         *
311         * @return The size of the arrangement.
312         */
313        protected Size2D arrangeRR(BlockContainer container, Graphics2D g2,
314                                   RectangleConstraint constraint) {
315    
316            Size2D size1 = arrange(container, g2, RectangleConstraint.NONE);
317    
318            if (constraint.getWidthRange().contains(size1.getWidth())) {
319                if (constraint.getHeightRange().contains(size1.getHeight())) {
320                    return size1;
321                }
322                else {
323                    // width is OK, but height must be constrained
324                    double h = constraint.getHeightRange().constrain(
325                            size1.getHeight());
326                    RectangleConstraint cc = new RectangleConstraint(
327                            size1.getWidth(), h);
328                    return arrangeFF(container, g2, cc);
329                }
330            }
331            else {
332                if (constraint.getHeightRange().contains(size1.getHeight())) {
333                    // height is OK, but width must be constrained
334                    double w = constraint.getWidthRange().constrain(
335                            size1.getWidth());
336                    RectangleConstraint cc = new RectangleConstraint(w,
337                            size1.getHeight());
338                    return arrangeFF(container, g2, cc);
339    
340                }
341                else {
342                    double w = constraint.getWidthRange().constrain(
343                            size1.getWidth());
344                    double h = constraint.getHeightRange().constrain(
345                            size1.getHeight());
346                    RectangleConstraint cc = new RectangleConstraint(w, h);
347                    return arrangeFF(container, g2, cc);
348                }
349            }
350        }
351    
352        /**
353         * Arrange with a fixed width and a height within a given range.
354         *
355         * @param container  the container.
356         * @param g2  the graphics device.
357         * @param constraint  the constraint.
358         *
359         * @return The size of the arrangement.
360         */
361        protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
362                                   RectangleConstraint constraint) {
363    
364            double width = constraint.getWidth() / this.columns;
365            RectangleConstraint bc = constraint.toFixedWidth(width);
366            List blocks = container.getBlocks();
367            double maxH = 0.0;
368            for (int r = 0; r < this.rows; r++) {
369                for (int c = 0; c < this.columns; c++) {
370                    int index = r * this.columns + c;
371                    if (index >= blocks.size()) {
372                        break;
373                    }
374                    Block b = (Block) blocks.get(index);
375                    if (b != null) {
376                        Size2D s = b.arrange(g2, bc);
377                        maxH = Math.max(maxH, s.getHeight());
378                    }
379                }
380            }
381            RectangleConstraint cc = constraint.toFixedHeight(maxH * this.rows);
382            return arrange(container, g2, cc);
383        }
384    
385        /**
386         * Arrange with a fixed height and no constraint for the width.
387         *
388         * @param container  the container.
389         * @param g2  the graphics device.
390         * @param constraint  the constraint.
391         *
392         * @return The size of the arrangement.
393         */
394        protected Size2D arrangeNF(BlockContainer container, Graphics2D g2,
395                                   RectangleConstraint constraint) {
396    
397            double height = constraint.getHeight() / this.rows;
398            RectangleConstraint bc = constraint.toFixedHeight(height);
399            List blocks = container.getBlocks();
400            double maxW = 0.0;
401            for (int r = 0; r < this.rows; r++) {
402                for (int c = 0; c < this.columns; c++) {
403                    int index = r * this.columns + c;
404                    if (index >= blocks.size()) {
405                        break;
406                    }
407                    Block b = (Block) blocks.get(index);
408                    if (b != null) {
409                        Size2D s = b.arrange(g2, bc);
410                        maxW = Math.max(maxW, s.getWidth());
411                    }
412                }
413            }
414            RectangleConstraint cc = constraint.toFixedWidth(maxW * this.columns);
415            return arrange(container, g2, cc);
416        }
417    
418        /**
419         * Clears any cached layout information retained by the arrangement.
420         */
421        public void clear() {
422            // nothing to clear
423        }
424    
425        /**
426         * Compares this layout manager for equality with an arbitrary object.
427         *
428         * @param obj  the object.
429         *
430         * @return A boolean.
431         */
432        public boolean equals(Object obj) {
433            if (obj == this) {
434                return true;
435            }
436            if (!(obj instanceof GridArrangement)) {
437                return false;
438            }
439            GridArrangement that = (GridArrangement) obj;
440            if (this.columns != that.columns) {
441                return false;
442            }
443            if (this.rows != that.rows) {
444                return false;
445            }
446            return true;
447        }
448    
449    }