/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-2012, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * GridArrangement.java * -------------------- * (C) Copyright 2005-2012, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * * Changes: * -------- * 08-Feb-2005 : Version 1 (DG); * 03-Dec-2008 : Implemented missing methods, and fixed bugs reported in * patch 2370487 (DG); * 17-Jun-2012 : Removed JCommon dependencies (DG); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.List; import org.jfree.chart.ui.Size2D; /** * Arranges blocks in a grid within their container. */ public class GridArrangement implements Arrangement, Serializable { /** For serialization. */ private static final long serialVersionUID = -2563758090144655938L; /** The rows. */ private int rows; /** The columns. */ private int columns; /** * Creates a new grid arrangement. * * @param rows the row count. * @param columns the column count. */ public GridArrangement(int rows, int columns) { this.rows = rows; this.columns = columns; } /** * Adds a block and a key which can be used to determine the position of * the block in the arrangement. This method is called by the container * (you don't need to call this method directly) and gives the arrangement * an opportunity to record the details if they are required. * * @param block the block. * @param key the key (<code>null</code> permitted). */ @Override public void add(Block block, Object key) { // can safely ignore } /** * Arranges the blocks within the specified container, subject to the given * constraint. * * @param container the container (<code>null</code> not permitted). * @param constraint the constraint. * @param g2 the graphics device. * * @return The size following the arrangement. */ @Override public Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { LengthConstraintType w = constraint.getWidthConstraintType(); LengthConstraintType h = constraint.getHeightConstraintType(); if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { return arrangeNN(container, g2); } else if (h == LengthConstraintType.FIXED) { return arrangeNF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { // find optimum height, then map to range return arrangeNR(container, g2, constraint); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { // find optimum height return arrangeFN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { return arrangeFF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { // find optimum height and map to range return arrangeFR(container, g2, constraint); } } else if (w == LengthConstraintType.RANGE) { // find optimum width and map to range if (h == LengthConstraintType.NONE) { // find optimum height return arrangeRN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { // fixed width return arrangeRF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { return arrangeRR(container, g2, constraint); } } throw new RuntimeException("Should never get to here!"); } /** * Arranges the container with no constraint on the width or height. * * @param container the container (<code>null</code> not permitted). * @param g2 the graphics device. * * @return The size. */ protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double maxW = 0.0; double maxH = 0.0; List<Block> blocks = container.getBlocks(); for (Block b : blocks) { if (b != null) { Size2D s = b.arrange(g2, RectangleConstraint.NONE); maxW = Math.max(maxW, s.width); maxH = Math.max(maxH, s.height); } } double width = this.columns * maxW; double height = this.rows * maxH; RectangleConstraint c = new RectangleConstraint(width, height); return arrangeFF(container, g2, c); } /** * Arranges the container with a fixed overall width and height. * * @param container the container (<code>null</code> not permitted). * @param g2 the graphics device. * @param constraint the constraint (<code>null</code> not permitted). * * @return The size following the arrangement. */ protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double width = constraint.getWidth() / this.columns; double height = constraint.getHeight() / this.rows; List<Block> blocks = container.getBlocks(); for (int c = 0; c < this.columns; c++) { for (int r = 0; r < this.rows; r++) { int index = r * this.columns + c; if (index >= blocks.size()) { break; } Block b = blocks.get(index); if (b != null) { b.setBounds(new Rectangle2D.Double(c * width, r * height, width, height)); } } } return new Size2D(this.columns * width, this.rows * height); } /** * Arrange with a fixed width and a height within a given range. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedHeight(); Size2D size1 = arrange(container, g2, c1); if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { double h = constraint.getHeightRange().constrain(size1.getHeight()); RectangleConstraint c2 = constraint.toFixedHeight(h); return arrange(container, g2, c2); } } /** * Arrange with a fixed height and a width within a given range. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedWidth(); Size2D size1 = arrange(container, g2, c1); if (constraint.getWidthRange().contains(size1.getWidth())) { return size1; } else { double w = constraint.getWidthRange().constrain(size1.getWidth()); RectangleConstraint c2 = constraint.toFixedWidth(w); return arrange(container, g2, c2); } } /** * Arrange with a fixed width and no height constraint. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeRN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedWidth(); Size2D size1 = arrange(container, g2, c1); if (constraint.getWidthRange().contains(size1.getWidth())) { return size1; } else { double w = constraint.getWidthRange().constrain(size1.getWidth()); RectangleConstraint c2 = constraint.toFixedWidth(w); return arrange(container, g2, c2); } } /** * Arrange with a fixed height and no width constraint. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeNR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedHeight(); Size2D size1 = arrange(container, g2, c1); if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { double h = constraint.getHeightRange().constrain(size1.getHeight()); RectangleConstraint c2 = constraint.toFixedHeight(h); return arrange(container, g2, c2); } } /** * Arrange with ranges for both the width and height constraints. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D size1 = arrange(container, g2, RectangleConstraint.NONE); if (constraint.getWidthRange().contains(size1.getWidth())) { if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { // width is OK, but height must be constrained double h = constraint.getHeightRange().constrain( size1.getHeight()); RectangleConstraint cc = new RectangleConstraint( size1.getWidth(), h); return arrangeFF(container, g2, cc); } } else { if (constraint.getHeightRange().contains(size1.getHeight())) { // height is OK, but width must be constrained double w = constraint.getWidthRange().constrain( size1.getWidth()); RectangleConstraint cc = new RectangleConstraint(w, size1.getHeight()); return arrangeFF(container, g2, cc); } else { double w = constraint.getWidthRange().constrain( size1.getWidth()); double h = constraint.getHeightRange().constrain( size1.getHeight()); RectangleConstraint cc = new RectangleConstraint(w, h); return arrangeFF(container, g2, cc); } } } /** * Arrange with a fixed width and a height within a given range. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size of the arrangement. */ protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double width = constraint.getWidth() / this.columns; RectangleConstraint bc = constraint.toFixedWidth(width); List<Block> blocks = container.getBlocks(); double maxH = 0.0; for (int r = 0; r < this.rows; r++) { for (int c = 0; c < this.columns; c++) { int index = r * this.columns + c; if (index >= blocks.size()) { break; } Block b = blocks.get(index); if (b != null) { Size2D s = b.arrange(g2, bc); maxH = Math.max(maxH, s.getHeight()); } } } RectangleConstraint cc = constraint.toFixedHeight(maxH * this.rows); return arrange(container, g2, cc); } /** * Arrange with a fixed height and no constraint for the width. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size of the arrangement. */ protected Size2D arrangeNF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double height = constraint.getHeight() / this.rows; RectangleConstraint bc = constraint.toFixedHeight(height); List<Block> blocks = container.getBlocks(); double maxW = 0.0; for (int r = 0; r < this.rows; r++) { for (int c = 0; c < this.columns; c++) { int index = r * this.columns + c; if (index >= blocks.size()) { break; } Block b = blocks.get(index); if (b != null) { Size2D s = b.arrange(g2, bc); maxW = Math.max(maxW, s.getWidth()); } } } RectangleConstraint cc = constraint.toFixedWidth(maxW * this.columns); return arrange(container, g2, cc); } /** * Clears any cached layout information retained by the arrangement. */ @Override public void clear() { // nothing to clear } /** * Compares this layout manager for equality with an arbitrary object. * * @param obj the object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GridArrangement)) { return false; } GridArrangement that = (GridArrangement) obj; if (this.columns != that.columns) { return false; } if (this.rows != that.rows) { return false; } return true; } }