content: /* ===========================================================
   * 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;
      }
  
  }
  