Package com.positive.charts.axis

Source Code of com.positive.charts.axis.CategoryAxis

package com.positive.charts.axis;

import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;

import com.positive.charts.axis.ticks.Tick;
import com.positive.charts.block.RectangleInsets;
import com.positive.charts.common.RectangleEdge;
import com.positive.charts.data.util.ObjectUtilities;
import com.positive.charts.data.util.RectangleAnchor;
import com.positive.charts.entity.CategoryLabelEntity;
import com.positive.charts.entity.EntityCollection;
import com.positive.charts.event.AxisChangeEvent;
import com.positive.charts.plot.CategoryPlot;
import com.positive.charts.plot.Plot;
import com.positive.charts.plot.PlotRenderingInfo;
import com.positive.charts.util.CategoryLabelWidthType;
import com.positive.charts.util.G2TextMeasurer;
import com.positive.charts.util.RectangleUtil;
import com.positive.charts.util.Size2D;
import com.positive.charts.util.TextBlock;
import com.positive.charts.util.TextUtilities;

/**
* An axis that displays categories.
*/
public class CategoryAxis extends BaseAxis {

  /** For serialization. */
  private static final long serialVersionUID = 5886554608114265863L;

  /**
   * The default margin for the axis (used for both lower and upper margins).
   */
  public static final double DEFAULT_AXIS_MARGIN = 0.05;

  /**
   * The default margin between categories (a percentage of the overall axis
   * length).
   */
  public static final double DEFAULT_CATEGORY_MARGIN = 0.20;

  /** The amount of space reserved at the start of the axis. */
  private double lowerMargin;

  /** The amount of space reserved at the end of the axis. */
  private double upperMargin;

  /** The amount of space reserved between categories. */
  private double categoryMargin;

  /** The maximum number of lines for category labels. */
  private int maximumCategoryLabelLines;

  /**
   * A ratio that is multiplied by the width of one category to determine the
   * maximum label width.
   */
  private float maximumCategoryLabelWidthRatio;

  /** The category label offset. */
  private int categoryLabelPositionOffset;

  /**
   * A structure defining the category label positions for each axis location.
   */
  private CategoryLabelPositions categoryLabelPositions;

  /** Storage for tick label font overrides (if any). */
  private Map tickLabelFontMap;

  /** Storage for tick label paint overrides (if any). */
  private transient Map tickLabelPaintMap;

  /** Storage for the category label tooltips (if any). */
  private Map categoryLabelToolTips;

  /**
   * Creates a new category axis with no label.
   */
  public CategoryAxis() {
    this(null);
  }

  /**
   * Constructs a category axis, using default values where necessary.
   *
   * @param label
   *            the axis label (<code>null</code> permitted).
   */
  public CategoryAxis(final String label) {
    super(label);

    this.lowerMargin = DEFAULT_AXIS_MARGIN;
    this.upperMargin = DEFAULT_AXIS_MARGIN;
    this.categoryMargin = DEFAULT_CATEGORY_MARGIN;
    this.maximumCategoryLabelLines = 1;
    this.maximumCategoryLabelWidthRatio = 0.0f;

    // setTickMarksVisible(false); // not supported by this axis type yet

    this.categoryLabelPositionOffset = 4;
    this.categoryLabelPositions = CategoryLabelPositions.STANDARD;
    this.tickLabelFontMap = new HashMap();
    this.tickLabelPaintMap = new HashMap();
    this.categoryLabelToolTips = new HashMap();

  }

  /**
   * Adds a tooltip to the specified category and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param category
   *            the category (<code>null<code> not permitted).
   * @param tooltip
   *            the tooltip text (<code>null</code> permitted).
   */
  public void addCategoryLabelToolTip(final Comparable category,
      final String tooltip) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    this.categoryLabelToolTips.put(category, tooltip);
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Calculates the size (width or height, depending on the location of the
   * axis) of a category gap.
   *
   * @param categoryCount
   *            the number of categories.
   * @param area
   *            the area within which the categories will be drawn.
   * @param edge
   *            the axis location.
   *
   * @return The category gap width.
   */
  protected double calculateCategoryGapSize(final int categoryCount,
      final Rectangle area, final RectangleEdge edge) {

    double result = 0.0;
    double available = 0.0;

    if ((edge == RectangleEdge.TOP) || (edge == RectangleEdge.BOTTOM)) {
      available = area.width;
    } else if ((edge == RectangleEdge.LEFT)
        || (edge == RectangleEdge.RIGHT)) {
      available = area.height;
    }

    if (categoryCount > 1) {
      result = available * this.getCategoryMargin() / (categoryCount - 1);
    }

    return result;

  }

  /**
   * Calculates the size (width or height, depending on the location of the
   * axis) of a category.
   *
   * @param categoryCount
   *            the number of categories.
   * @param area
   *            the area within which the categories will be drawn.
   * @param edge
   *            the axis location.
   *
   * @return The category size.
   */
  protected double calculateCategorySize(final int categoryCount,
      final Rectangle area, final RectangleEdge edge) {

    double result = 0.0;
    double available = 0.0;

    if ((edge == RectangleEdge.TOP) || (edge == RectangleEdge.BOTTOM)) {
      available = area.width;
    } else if ((edge == RectangleEdge.LEFT)
        || (edge == RectangleEdge.RIGHT)) {
      available = area.height;
    }
    if (categoryCount > 1) {
      result = available
          * (1 - this.getLowerMargin() - this.getUpperMargin() - this
              .getCategoryMargin());
      result = result / categoryCount;
    } else {
      result = available
          * (1 - this.getLowerMargin() - this.getUpperMargin());
    }
    return result;

  }

  /**
   * A utility method for determining the height of a text block.
   *
   * @param block
   *            the text block.
   * @param position
   *            the label position.
   * @param g2
   *            the graphics device.
   *
   * @return The height.
   */
  protected double calculateTextBlockHeight(final TextBlock block,
      final CategoryLabelPosition position, final GC g2) {

    final RectangleInsets insets = this.getTickLabelInsets();
    final Size2D size = block.calculateDimensions(g2);
    final Rectangle box = RectangleUtil.Double(0.0, 0.0, size.width,
        size.height);
    // Shape rotatedBox = ShapeUtilities.rotateShape(
    // box, position.getAngle(), 0.0f, 0.0f
    // );
    final Rectangle rotatedBox = box;
    final double h = rotatedBox.height + insets.getTop()
        + insets.getBottom();
    return h;

  }

  /**
   * A utility method for determining the width of a text block.
   *
   * @param block
   *            the text block.
   * @param position
   *            the position.
   * @param g2
   *            the graphics device.
   *
   * @return The width.
   */
  protected double calculateTextBlockWidth(final TextBlock block,
      final CategoryLabelPosition position, final GC g2) {

    final RectangleInsets insets = this.getTickLabelInsets();
    final Size2D size = block.calculateDimensions(g2);
    final Rectangle box = RectangleUtil.Double(0.0, 0.0, size.width,
        size.height);
    // TODO : support rotation
    // Shape rotatedBox = ShapeUtilities.rotateShape(
    // box, position.getAngle(), 0.0f, 0.0f
    // );
    final Rectangle rotatedBox = box;
    final double w = rotatedBox.width + insets.getTop()
        + insets.getBottom();
    return w;

  }

  /**
   * Clears the category label tooltips and sends an {@link AxisChangeEvent}
   * to all registered listeners.
   */
  public void clearCategoryLabelToolTips() {
    this.categoryLabelToolTips.clear();
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Creates a clone of the axis.
   *
   * @return A clone.
   *
   * @throws CloneNotSupportedException
   *             if some component of the axis does not support cloning.
   */
  public Object clone() throws CloneNotSupportedException {
    final CategoryAxis clone = (CategoryAxis) super.clone();
    clone.tickLabelFontMap = new HashMap(this.tickLabelFontMap);
    clone.tickLabelPaintMap = new HashMap(this.tickLabelPaintMap);
    clone.categoryLabelToolTips = new HashMap(this.categoryLabelToolTips);
    return clone;
  }

  /**
   * Configures the axis against the current plot.
   */
  public void configure() {
    // nothing required
  }

  /**
   * Creates a label.
   *
   * @param category
   *            the category.
   * @param width
   *            the available width.
   * @param edge
   *            the edge on which the axis appears.
   * @param g2
   *            the graphics device.
   *
   * @return A label.
   */
  protected TextBlock createLabel(final Comparable category,
      final float width, final RectangleEdge edge, final GC g2) {
    final TextBlock label = TextUtilities.createTextBlock(category
        .toString(), this.getTickLabelFont(category), this
        .getTickLabelPaint(category), width,
        this.maximumCategoryLabelLines, new G2TextMeasurer(g2));
    return label;
  }

  /**
   * Draws the axis on a Java 2D graphics device (such as the screen or a
   * printer).
   *
   * @param g2
   *            the graphics device (<code>null</code> not permitted).
   * @param cursor
   *            the cursor location.
   * @param plotArea
   *            the area within which the axis should be drawn (
   *            <code>null</code> not permitted).
   * @param dataArea
   *            the area within which the plot is being drawn (
   *            <code>null</code> not permitted).
   * @param edge
   *            the location of the axis (<code>null</code> not permitted).
   * @param plotState
   *            collects information about the plot (<code>null</code>
   *            permitted).
   *
   * @return The axis state (never <code>null</code>).
   */
  public AxisState draw(final GC g2, final double cursor,
      final Rectangle plotArea, final Rectangle dataArea,
      final RectangleEdge edge, final PlotRenderingInfo plotState) {

    // if the axis is not visible, don't draw it...
    if (!this.isVisible()) {
      return new AxisState(cursor);
    }

    if (this.isAxisLineVisible()) {
      this.drawAxisLine(g2, (int) (cursor + 0.5), dataArea, edge);
    }

    // draw the category labels and axis label
    AxisState state = new AxisState(cursor);
    state = this.drawCategoryLabels(g2, plotArea, dataArea, edge, state,
        plotState);
    state = this.drawLabel(this.getLabel(), g2, plotArea, dataArea, edge,
        state);

    return state;

  }

  /**
   * Draws the category labels and returns the updated axis state.
   *
   * @param g2
   *            the graphics device (<code>null</code> not permitted).
   * @param plotArea
   *            the plot area (<code>null</code> not permitted).
   * @param dataArea
   *            the area inside the axes (<code>null</code> not permitted).
   * @param edge
   *            the axis location (<code>null</code> not permitted).
   * @param state
   *            the axis state (<code>null</code> not permitted).
   * @param plotState
   *            collects information about the plot (<code>null</code>
   *            permitted).
   *
   * @return The updated axis state (never <code>null</code>).
   */
  protected AxisState drawCategoryLabels(final GC g2,
      final Rectangle plotArea, final Rectangle dataArea,
      final RectangleEdge edge, final AxisState state,
      final PlotRenderingInfo plotState) {

    if (state == null) {
      throw new IllegalArgumentException("Null 'state' argument.");
    }

    if (this.isTickLabelsVisible()) {
      final List ticks = this.refreshTicks(g2, state, plotArea, edge);
      state.setTicks(ticks);

      int categoryIndex = 0;
      final Iterator iterator = ticks.iterator();
      while (iterator.hasNext()) {

        final CategoryTick tick = (CategoryTick) iterator.next();
        g2.setFont(this.getTickLabelFont(tick.getCategory()));
        g2.setForeground(this.getTickLabelPaint(tick.getCategory()));

        final CategoryLabelPosition position = this.categoryLabelPositions
            .getLabelPosition(edge);
        double x0 = 0.0;
        double x1 = 0.0;
        double y0 = 0.0;
        double y1 = 0.0;
        if (edge == RectangleEdge.TOP) {
          x0 = this.getCategoryStart(categoryIndex, ticks.size(),
              dataArea, edge);
          x1 = this.getCategoryEnd(categoryIndex, ticks.size(),
              dataArea, edge);
          y1 = state.getCursor() - this.categoryLabelPositionOffset;
          y0 = y1 - state.getMax();
        } else if (edge == RectangleEdge.BOTTOM) {
          x0 = this.getCategoryStart(categoryIndex, ticks.size(),
              dataArea, edge);
          x1 = this.getCategoryEnd(categoryIndex, ticks.size(),
              dataArea, edge);
          y0 = state.getCursor() + this.categoryLabelPositionOffset;
          y1 = y0 + state.getMax();
        } else if (edge == RectangleEdge.LEFT) {
          y0 = this.getCategoryStart(categoryIndex, ticks.size(),
              dataArea, edge);
          y1 = this.getCategoryEnd(categoryIndex, ticks.size(),
              dataArea, edge);
          x1 = state.getCursor() - this.categoryLabelPositionOffset;
          x0 = x1 - state.getMax();
        } else if (edge == RectangleEdge.RIGHT) {
          y0 = this.getCategoryStart(categoryIndex, ticks.size(),
              dataArea, edge);
          y1 = this.getCategoryEnd(categoryIndex, ticks.size(),
              dataArea, edge);
          x0 = state.getCursor() + this.categoryLabelPositionOffset;
          x1 = x0 - state.getMax();
        }
        final Rectangle area = RectangleUtil.Double(x0, y0, (x1 - x0),
            (y1 - y0));
        final Point anchorPoint = RectangleAnchor.coordinates(area,
            position.getCategoryAnchor());
        final TextBlock block = tick.getLabel();
        block.draw(g2, anchorPoint.x, anchorPoint.y, position
            .getLabelAnchor(), 0, 0, position.getAngle());
        final Rectangle bounds = block.calculateBounds(g2,
            anchorPoint.x, anchorPoint.y,
            position.getLabelAnchor(), anchorPoint.x,
            anchorPoint.y, position.getAngle());

        if ((plotState != null) && (plotState.getOwner() != null)) {
          final EntityCollection entities = plotState.getOwner()
              .getEntityCollection();
          if (entities != null) {
            final String tooltip = this
                .getCategoryLabelToolTip(tick.getCategory());

            final Region boundRegion = new Region();
            boundRegion.add(bounds);
            entities
                .add(new CategoryLabelEntity(
                    tick.getCategory(), boundRegion,
                    tooltip, null));
          }
        }
        categoryIndex++;
      }

      if (edge.equals(RectangleEdge.TOP)) {
        final double h = state.getMax();
        state.cursorUp(h);
      } else if (edge.equals(RectangleEdge.BOTTOM)) {
        final double h = state.getMax();
        state.cursorDown(h);
      } else if (edge == RectangleEdge.LEFT) {
        final double w = state.getMax();
        state.cursorLeft(w);
      } else if (edge == RectangleEdge.RIGHT) {
        final double w = state.getMax();
        state.cursorRight(w);
      }
    }
    return state;
  }

  /**
   * Draws the category labels and returns the updated axis state.
   *
   * @param g2
   *            the graphics device (<code>null</code> not permitted).
   * @param dataArea
   *            the area inside the axes (<code>null</code> not permitted).
   * @param edge
   *            the axis location (<code>null</code> not permitted).
   * @param state
   *            the axis state (<code>null</code> not permitted).
   * @param plotState
   *            collects information about the plot (<code>null</code>
   *            permitted).
   *
   * @return The updated axis state (never <code>null</code>).
   *
   * @deprecated Use
   *             {@link #drawCategoryLabels(Graphics2D, Rectangle2D, Rectangle2D, RectangleEdge, AxisState, PlotRenderingInfo)}
   *             .
   */
  protected AxisState drawCategoryLabels(final GC g2,
      final Rectangle dataArea, final RectangleEdge edge,
      final AxisState state, final PlotRenderingInfo plotState) {

    // this method is deprecated because we really need the plotArea
    // when drawing the labels - see bug 1277726
    return this.drawCategoryLabels(g2, dataArea, dataArea, edge, state,
        plotState);
  }

  /**
   * Tests this axis for equality with an arbitrary object.
   *
   * @param obj
   *            the object (<code>null</code> permitted).
   *
   * @return A boolean.
   */
  public boolean equals(final Object obj) {
    if (obj == this) {
      return true;
    }
    if (!(obj instanceof CategoryAxis)) {
      return false;
    }
    if (!super.equals(obj)) {
      return false;
    }
    final CategoryAxis that = (CategoryAxis) obj;
    if (that.lowerMargin != this.lowerMargin) {
      return false;
    }
    if (that.upperMargin != this.upperMargin) {
      return false;
    }
    if (that.categoryMargin != this.categoryMargin) {
      return false;
    }
    if (that.maximumCategoryLabelWidthRatio != this.maximumCategoryLabelWidthRatio) {
      return false;
    }
    if (that.categoryLabelPositionOffset != this.categoryLabelPositionOffset) {
      return false;
    }
    if (!ObjectUtilities.equal(that.categoryLabelPositions,
        this.categoryLabelPositions)) {
      return false;
    }
    if (!ObjectUtilities.equal(that.categoryLabelToolTips,
        this.categoryLabelToolTips)) {
      return false;
    }
    if (!ObjectUtilities
        .equal(this.tickLabelFontMap, that.tickLabelFontMap)) {
      return false;
    }
    // TODO : SERIALIZATION : Fix below
    // if (!equalPaintMaps(this.tickLabelPaintMap, that.tickLabelPaintMap))
    // {
    // return false;
    // }
    return true;
  }

  /**
   * Returns the end coordinate for the specified category.
   *
   * @param category
   *            the category.
   * @param categoryCount
   *            the number of categories.
   * @param area
   *            the data area.
   * @param edge
   *            the axis location.
   *
   * @return The coordinate.
   */
  public double getCategoryEnd(final int category, final int categoryCount,
      final Rectangle area, final RectangleEdge edge) {

    return this.getCategoryStart(category, categoryCount, area, edge)
        + this.calculateCategorySize(categoryCount, area, edge);

  }

  // TODO: Change method name
  /**
   *
   * Returns the Java 2D coordinate for a category.
   *
   * @param anchor
   *            the anchor point.
   * @param category
   *            the category index.
   * @param categoryCount
   *            the category count.
   * @param area
   *            the data area.
   * @param edge
   *            the location of the axis.
   *
   * @return The coordinate.
   */
  public double getCategoryJava2DCoordinate(final CategoryAnchor anchor,
      final int category, final int categoryCount, final Rectangle area,
      final RectangleEdge edge) {

    double result = 0.0;
    if (anchor == CategoryAnchor.START) {
      result = this.getCategoryStart(category, categoryCount, area, edge);
    } else if (anchor == CategoryAnchor.MIDDLE) {
      result = this
          .getCategoryMiddle(category, categoryCount, area, edge);
    } else if (anchor == CategoryAnchor.END) {
      result = this.getCategoryEnd(category, categoryCount, area, edge);
    }
    return result;

  }

  /**
   * Returns the offset between the axis and the category labels (before label
   * positioning is taken into account).
   *
   * @return The offset (in Java2D units).
   */
  public int getCategoryLabelPositionOffset() {
    return this.categoryLabelPositionOffset;
  }

  /**
   * Returns the category label position specification (this contains label
   * positioning info for all four possible axis locations).
   *
   * @return The positions (never <code>null</code>).
   */
  public CategoryLabelPositions getCategoryLabelPositions() {
    return this.categoryLabelPositions;
  }

  /**
   * Returns the tool tip text for the label belonging to the specified
   * category.
   *
   * @param category
   *            the category (<code>null</code> not permitted).
   *
   * @return The tool tip text (possibly <code>null</code>).
   */
  public String getCategoryLabelToolTip(final Comparable category) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    return (String) this.categoryLabelToolTips.get(category);
  }

  /**
   * Returns the category margin.
   *
   * @return The margin.
   */
  public double getCategoryMargin() {
    return this.categoryMargin;
  }

  /**
   * Returns the middle coordinate for the specified category.
   *
   * @param category
   *            the category.
   * @param categoryCount
   *            the number of categories.
   * @param area
   *            the data area.
   * @param edge
   *            the axis location.
   *
   * @return The coordinate.
   */
  public double getCategoryMiddle(final int category,
      final int categoryCount, final Rectangle area,
      final RectangleEdge edge) {

    return this.getCategoryStart(category, categoryCount, area, edge)
        + this.calculateCategorySize(categoryCount, area, edge) / 2;

  }

  /**
   * Returns the starting coordinate for the specified category.
   *
   * @param category
   *            the category.
   * @param categoryCount
   *            the number of categories.
   * @param area
   *            the data area.
   * @param edge
   *            the axis location.
   *
   * @return The coordinate.
   */
  public double getCategoryStart(final int category, final int categoryCount,
      final Rectangle area, final RectangleEdge edge) {

    double result = 0.0;
    if ((edge == RectangleEdge.TOP) || (edge == RectangleEdge.BOTTOM)) {
      result = area.x + area.width * this.getLowerMargin();
    } else if ((edge == RectangleEdge.LEFT)
        || (edge == RectangleEdge.RIGHT)) {

      result = RectangleUtil.getMinY(area) + area.height
          * this.getLowerMargin();
    }

    final double categorySize = this.calculateCategorySize(categoryCount,
        area, edge);
    final double categoryGapWidth = this.calculateCategoryGapSize(
        categoryCount, area, edge);

    result = result + category * (categorySize + categoryGapWidth);

    return result;
  }

  /**
   * Returns the lower margin for the axis.
   *
   * @return The margin.
   */
  public double getLowerMargin() {
    return this.lowerMargin;
  }

  /**
   * Returns the maximum number of lines to use for each category label.
   *
   * @return The maximum number of lines.
   */
  public int getMaximumCategoryLabelLines() {
    return this.maximumCategoryLabelLines;
  }

  /**
   * Returns the category label width ratio.
   *
   * @return The ratio.
   */
  public float getMaximumCategoryLabelWidthRatio() {
    return this.maximumCategoryLabelWidthRatio;
  }

  /**
   * Returns the font for the tick label for the given category.
   *
   * @param category
   *            the category (<code>null</code> not permitted).
   *
   * @return The font (never <code>null</code>).
   */
  public Font getTickLabelFont(final Comparable category) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    Font result = (Font) this.tickLabelFontMap.get(category);
    // if there is no specific font, use the general one...
    if (result == null) {
      result = this.getTickLabelFont();
    }
    if (result == null) {

    }
    return result;
  }

  /**
   * Returns the paint for the tick label for the given category.
   *
   * @param category
   *            the category (<code>null</code> not permitted).
   *
   * @return The paint (never <code>null</code>).
   */
  public Color getTickLabelPaint(final Comparable category) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    Color result = (Color) this.tickLabelPaintMap.get(category);
    // if there is no specific paint, use the general one...
    if (result == null) {
      result = this.getTickLabelPaint();
    }
    return result;
  }

  /**
   * Returns the upper margin for the axis.
   *
   * @return The margin.
   */
  public double getUpperMargin() {
    return this.upperMargin;
  }

  /**
   * Returns a hash code for this object.
   *
   * @return A hash code.
   */
  public int hashCode() {
    if (this.getLabel() != null) {
      return this.getLabel().hashCode();
    } else {
      return 0;
    }
  }

  /**
   * Creates a temporary list of ticks that can be used when drawing the axis.
   *
   * @param g2
   *            the graphics device (used to get font measurements).
   * @param state
   *            the axis state.
   * @param dataArea
   *            the area inside the axes.
   * @param edge
   *            the location of the axis.
   *
   * @return A list of ticks.
   */
  public List refreshTicks(final GC g2, final AxisState state,
      final Rectangle dataArea, final RectangleEdge edge) {

    final List ticks = new java.util.ArrayList();

    // sanity check for data area...
    if ((dataArea.height <= 0.0) || (dataArea.width < 0.0)) {
      return ticks;
    }

    final CategoryPlot plot = (CategoryPlot) this.getPlot();
    final List categories = plot.getCategoriesForAxis(this);
    double max = 0.0;

    if (categories != null) {
      final CategoryLabelPosition position = this.categoryLabelPositions
          .getLabelPosition(edge);
      float r = this.maximumCategoryLabelWidthRatio;
      if (r <= 0.0) {
        r = position.getWidthRatio();
      }

      float l = 0.0f;
      if (position.getWidthType() == CategoryLabelWidthType.CATEGORY) {
        l = (float) this.calculateCategorySize(categories.size(),
            dataArea, edge);
      } else {
        if (RectangleEdge.isLeftOrRight(edge)) {
          l = dataArea.width;
        } else {
          l = dataArea.height;
        }
      }
      int categoryIndex = 0;
      final Iterator iterator = categories.iterator();
      while (iterator.hasNext()) {
        final Comparable category = (Comparable) iterator.next();
        final TextBlock label = this.createLabel(category, l * r, edge,
            g2);
        if ((edge == RectangleEdge.TOP)
            || (edge == RectangleEdge.BOTTOM)) {
          max = Math.max(max, this.calculateTextBlockHeight(label,
              position, g2));
        } else if ((edge == RectangleEdge.LEFT)
            || (edge == RectangleEdge.RIGHT)) {
          max = Math.max(max, this.calculateTextBlockWidth(label,
              position, g2));
        }
        final Tick tick = new CategoryTick(category, label, position
            .getLabelAnchor(), position.getRotationAnchor(),
            position.getAngle());
        ticks.add(tick);
        categoryIndex = categoryIndex + 1;
      }
    }
    state.setMax(max);
    return ticks;

  }

  /**
   * Removes the tooltip for the specified category and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param category
   *            the category (<code>null<code> not permitted).
   */
  public void removeCategoryLabelToolTip(final Comparable category) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    this.categoryLabelToolTips.remove(category);
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Estimates the space required for the axis, given a specific drawing area.
   *
   * @param g2
   *            the graphics device (used to obtain font information).
   * @param plot
   *            the plot that the axis belongs to.
   * @param plotArea
   *            the area within which the axis should be drawn.
   * @param edge
   *            the axis location (top or bottom).
   * @param space
   *            the space already reserved.
   *
   * @return The space required to draw the axis.
   */
  public AxisSpace reserveSpace(final GC g2, final Plot plot,
      final Rectangle plotArea, final RectangleEdge edge, AxisSpace space) {

    // create a new space object if one wasn't supplied...
    if (space == null) {
      space = new AxisSpace();
    }

    // if the axis is not visible, no additional space is required...
    if (!this.isVisible()) {
      return space;
    }

    // calculate the max size of the tick labels (if visible)...
    double tickLabelHeight = 0.0;
    double tickLabelWidth = 0.0;
    if (this.isTickLabelsVisible()) {
      g2.setFont(this.getTickLabelFont());
      final AxisState state = new AxisState();
      // we call refresh ticks just to get the maximum width or height
      this.refreshTicks(g2, state, plotArea, edge);
      if (edge == RectangleEdge.TOP) {
        tickLabelHeight = state.getMax();
      } else if (edge == RectangleEdge.BOTTOM) {
        tickLabelHeight = state.getMax();
      } else if (edge == RectangleEdge.LEFT) {
        tickLabelWidth = state.getMax();
      } else if (edge == RectangleEdge.RIGHT) {
        tickLabelWidth = state.getMax();
      }
    }

    // get the axis label size and update the space object...
    final Rectangle labelEnclosure = this.getLabelEnclosure(g2, edge);
    double labelHeight = 0.0;
    double labelWidth = 0.0;
    if (RectangleEdge.isTopOrBottom(edge)) {
      labelHeight = labelEnclosure.height;
      space.add((int) (labelHeight + tickLabelHeight
          + this.categoryLabelPositionOffset + 0.5), edge);
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      labelWidth = labelEnclosure.width;
      space.add((int) (labelWidth + tickLabelWidth
          + this.categoryLabelPositionOffset + 0.5), edge);
    }
    return space;

  }

  /**
   * Sets the offset between the axis and the category labels (before label
   * positioning is taken into account).
   *
   * @param offset
   *            the offset (in Java2D units).
   */
  public void setCategoryLabelPositionOffset(final int offset) {
    this.categoryLabelPositionOffset = offset;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the category label position specification for the axis and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param positions
   *            the positions (<code>null</code> not permitted).
   */
  public void setCategoryLabelPositions(final CategoryLabelPositions positions) {
    if (positions == null) {
      throw new IllegalArgumentException("Null 'positions' argument.");
    }
    this.categoryLabelPositions = positions;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the category margin and sends an {@link AxisChangeEvent} to all
   * registered listeners. The overall category margin is distributed over N-1
   * gaps, where N is the number of categories on the axis.
   *
   * @param margin
   *            the margin as a percentage of the axis length (for example,
   *            0.05 is five percent).
   */
  public void setCategoryMargin(final double margin) {
    this.categoryMargin = margin;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the lower margin for the axis and sends an {@link AxisChangeEvent}
   * to all registered listeners.
   *
   * @param margin
   *            the margin as a percentage of the axis length (for example,
   *            0.05 is five percent).
   */
  public void setLowerMargin(final double margin) {
    this.lowerMargin = margin;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the maximum number of lines to use for each category label and sends
   * an {@link AxisChangeEvent} to all registered listeners.
   *
   * @param lines
   *            the maximum number of lines.
   */
  public void setMaximumCategoryLabelLines(final int lines) {
    this.maximumCategoryLabelLines = lines;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the maximum category label width ratio and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param ratio
   *            the ratio.
   */
  public void setMaximumCategoryLabelWidthRatio(final float ratio) {
    this.maximumCategoryLabelWidthRatio = ratio;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the font for the tick label for the specified category and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param category
   *            the category (<code>null</code> not permitted).
   * @param font
   *            the font (<code>null</code> permitted).
   */
  public void setTickLabelFont(final Comparable category, final Font font) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    if (font == null) {
      this.tickLabelFontMap.remove(category);
    } else {
      this.tickLabelFontMap.put(category, font);
    }
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the paint for the tick label for the specified category and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param category
   *            the category (<code>null</code> not permitted).
   * @param paint
   *            the paint (<code>null</code> permitted).
   */
  public void setTickLabelPaint(final Comparable category, final Color paint) {
    if (category == null) {
      throw new IllegalArgumentException("Null 'category' argument.");
    }
    if (paint == null) {
      this.tickLabelPaintMap.remove(category);
    } else {
      this.tickLabelPaintMap.put(category, paint);
    }
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the upper margin for the axis and sends an {@link AxisChangeEvent}
   * to all registered listeners.
   *
   * @param margin
   *            the margin as a percentage of the axis length (for example,
   *            0.05 is five percent).
   */
  public void setUpperMargin(final double margin) {
    this.upperMargin = margin;
    this.notifyListeners(new AxisChangeEvent(this));
  }
}
TOP

Related Classes of com.positive.charts.axis.CategoryAxis

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.