Added support for individual tick label font and paint...

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
padhu
Posts: 7
Joined: Tue Jun 07, 2005 8:31 pm

Added support for individual tick label font and paint...

Post by padhu » Thu Jun 09, 2005 1:13 pm

I have extended the CategoryAxis class to add support to setting font and paint to individual tick labels. The new methods are

setTickLabelFont(Comparable category, Font font)
setTickLabelPaint(Comparable category, Paint paint)

If no font or paint is found for the category, the default font/paint is used.

The methods overwritten are an exact copy of the implementation in CategoryAxis with the required changes. I had to implement the category tool tip code again because the categoryToolTips Collection object is not accessible ( no get/set ) in CategoryAxis.

Maybe this code can be incorporated into CategoryAxis code for a future release.

Code: Select all


import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.entity.TickLabelEntity;
import org.jfree.chart.event.AxisChangeEvent;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.text.G2TextMeasurer;
import org.jfree.text.TextBlock;
import org.jfree.text.TextUtilities;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.Size2D;
import org.jfree.util.Log;
import org.jfree.util.LogContext;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.ShapeUtilities;
import org.jfree.chart.axis.*;
import java.awt.*;

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

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

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

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

    /**
     * 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(Comparable category, String tooltip) {
        if (category == null) {
            throw new IllegalArgumentException("Null 'category' argument.");
        }
        this.categoryLabelToolTips.put(category, tooltip);
        notifyListeners(new AxisChangeEvent(this));
    }

    /**
     * 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(Comparable category) {
        if (category == null) {
            throw new IllegalArgumentException("Null 'category' argument.");
        }
        this.categoryLabelToolTips.remove(category);
        notifyListeners(new AxisChangeEvent(this));
    }

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

    /**
     * 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(Graphics2D g2,
                                           Rectangle2D plotArea,
                                           Rectangle2D dataArea,
                                           RectangleEdge edge,
                                           AxisState state,
                                           PlotRenderingInfo plotState) {

        if (logger.isDebugEnabled()) {
            logger.debug(
                "Entering drawCategoryLabels() method, cursor = "
                + state.getCursor()
            );
        }
        if (state == null) {
            throw new IllegalArgumentException("Null 'state' argument.");
        }

        if (isTickLabelsVisible()) {
//            g2.setFont(getTickLabelFont());
//            g2.setPaint(getTickLabelPaint());
            List ticks = refreshTicks(g2, state, plotArea, dataArea, edge);
            state.setTicks(ticks);

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

                CategoryTick tick = (CategoryTick) iterator.next();
//                g2.setPaint(getTickLabelPaint());
                g2.setFont(getTickLabelFont(tick.getCategory()));
                g2.setPaint(getTickLabelPaint(tick.getCategory()));

                CategoryLabelPosition position
                    = this.getCategoryLabelPositions().getLabelPosition(edge);
                double x0 = 0.0;
                double x1 = 0.0;
                double y0 = 0.0;
                double y1 = 0.0;
                if (edge == RectangleEdge.TOP) {
                    x0 = getCategoryStart(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    x1 = getCategoryEnd(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    y1 = state.getCursor() - this.getCategoryLabelPositionOffset();
                    y0 = y1 - state.getMax();
                }
                else if (edge == RectangleEdge.BOTTOM) {
                    x0 = getCategoryStart(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    x1 = getCategoryEnd(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    y0 = state.getCursor() + this.getCategoryLabelPositionOffset();
                    y1 = y0 + state.getMax();
                }
                else if (edge == RectangleEdge.LEFT) {
                    y0 = getCategoryStart(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    y1 = getCategoryEnd(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    x1 = state.getCursor() - this.getCategoryLabelPositionOffset();
                    x0 = x1 - state.getMax();
                }
                else if (edge == RectangleEdge.RIGHT) {
                    y0 = getCategoryStart(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    y1 = getCategoryEnd(
                        categoryIndex, ticks.size(), dataArea, edge
                    );
                    x0 = state.getCursor() + this.getCategoryLabelPositionOffset();
                    x1 = x0 - state.getMax();
                }
                Rectangle2D area = new Rectangle2D.Double(
                    x0, y0, (x1 - x0), (y1 - y0)
                );
                Point2D anchorPoint = RectangleAnchor.coordinates(
                    area, position.getCategoryAnchor()
                );
                TextBlock block = tick.getLabel();
                block.draw(
                    g2,
                    (float) anchorPoint.getX(), (float) anchorPoint.getY(),
                    position.getLabelAnchor(),
                    (float) anchorPoint.getX(), (float) anchorPoint.getY(),
                    position.getAngle()
                );
                Shape bounds = block.calculateBounds(
                    g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
                    position.getLabelAnchor(),
                    (float) anchorPoint.getX(), (float) anchorPoint.getY(),
                    position.getAngle()
                );
                if (plotState != null) {
                    EntityCollection entities
                        = plotState.getOwner().getEntityCollection();
                    if (entities != null) {
                        String tooltip
                            = (String) this.categoryLabelToolTips.get(
                                tick.getCategory()
                            );
                        entities.add(
                            new TickLabelEntity(bounds, tooltip, null)
                        );
                    }
                }
                categoryIndex++;
            }

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

    /**
     * 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 plotArea  the area where the plot and axes will be drawn.
     * @param dataArea  the area inside the axes.
     * @param edge  the location of the axis.
     *
     * @return A list of ticks.
     */
    public List refreshTicks(Graphics2D g2,
                             AxisState state,
                             Rectangle2D plotArea,
                             Rectangle2D dataArea,
                             RectangleEdge edge) {

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

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

        CategoryPlot plot = (CategoryPlot) getPlot();
        List categories = plot.getCategories();
        double max = 0.0;

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

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

    }

    /**
     * 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(Comparable category, float width,
                                    RectangleEdge edge, Graphics2D g2) {
        TextBlock label = TextUtilities.createTextBlock(
            category.toString(), getTickLabelFont(category), getTickLabelPaint(category),
            width, this.getMaximumCategoryLabelLines(), new G2TextMeasurer(g2)
        );
        return label;
    }


    private Map tickLabelFonts;
    public void setTickLabelFont(Comparable category, Font font) {
      if ( tickLabelFonts == null )
        tickLabelFonts = new HashMap();
      tickLabelFonts.put(category, font);
    }

    public Font getTickLabelFont(Comparable category) {
      if ( tickLabelFonts == null )
        return getTickLabelFont();
      Font font = (Font)tickLabelFonts.get(category);
      return font != null?font:getTickLabelFont();
    }
    private Map tickLabelColors;
    public void setTickLabelPaint(Comparable category, Color color) {
      if ( tickLabelColors == null )
        tickLabelColors = new HashMap();
      tickLabelColors.put(category, color);
    }

    public Paint getTickLabelPaint(Comparable category) {
      if ( tickLabelColors == null )
        return getTickLabelPaint();
      Paint paint = (Paint)tickLabelColors.get(category);
      return paint != null?paint:getTickLabelPaint();
    }

}

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Thu Jun 09, 2005 4:20 pm

Can you post this in the Patch Manager at SourceForge so it doesn't get lost?
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

padhu
Posts: 7
Joined: Tue Jun 07, 2005 8:31 pm

Posted in SourceForge...

Post by padhu » Thu Jun 09, 2005 6:40 pm

Posted in Sourcefoge.


Thanks

-- pady

Locked