org.jax.maanova.plot.MaanovaChartPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.jax.maanova.plot.MaanovaChartPanel.java

Source

/*
 * Copyright (c) 2010 The Jackson Laboratory
 * 
 * This is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.jax.maanova.plot;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.swing.JPanel;

import org.jax.maanova.Maanova;
import org.jax.util.concurrent.MultiTaskProgressPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.XYPlot;

/**
 * A chart panel class with some extra functionality for J/Maanova
 * @author <A HREF="mailto:keith.sheppard@jax.org">Keith Sheppard</A>
 */
public class MaanovaChartPanel extends JPanel {
    /**
     * every {@link java.io.Serializable} is supposed to have one of these
     */
    private static final long serialVersionUID = -2265706893499823906L;

    private Rectangle dragRectangle = null;

    private MouseListener selfMouseListener = new MouseAdapter() {
        /**
         * {@inheritDoc}
         */
        @Override
        public void mousePressed(MouseEvent e) {
            MaanovaChartPanel.this.myMousePressed(e);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void mouseReleased(MouseEvent e) {
            MaanovaChartPanel.this.myMouseReleased(e);
        }
    };

    private MouseMotionListener selfMotionListener = new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            MaanovaChartPanel.this.myMouseDragged(e);
        }
    };

    private final Runnable getNextChartRunnable = new Runnable() {
        public void run() {
            while (true) {
                MaanovaChartPanel.this.chartImage = MaanovaChartPanel.this.renderChartImageTask.getNextImage();
                MaanovaChartPanel.this.repaint();
            }
        }
    };

    private final ConcurrentLinkedQueue<AreaSelectionListener> areaSelectionListeners = new ConcurrentLinkedQueue<AreaSelectionListener>();

    private final ChartRenderingInfo chartRenderingInfo;

    private final RenderChartImageTask renderChartImageTask;

    private final ComponentListener selfComponentListener = new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            MaanovaChartPanel.this.selfResized();
        }
    };

    private volatile BufferedImage chartImage = null;

    private static final Color DEFAULT_SELECTION_RECTANGLE_COLOR = Color.RED;

    /**
     * @see #getSelectionRectangleColor()
     */
    private final Color selectionRectangleColor = DEFAULT_SELECTION_RECTANGLE_COLOR;

    /**
     * Same as the border color except translucent
     */
    private static final Color DEFAULT_SELECTION_RECTANGLE_FILL_COLOR = new Color(
            DEFAULT_SELECTION_RECTANGLE_COLOR.getRed(), DEFAULT_SELECTION_RECTANGLE_COLOR.getGreen(),
            DEFAULT_SELECTION_RECTANGLE_COLOR.getBlue(), DEFAULT_SELECTION_RECTANGLE_COLOR.getAlpha() / 16);

    /**
     * @see #getSelectionRectangleFillColor()
     */
    private final Color selectionRectangleFillColor = DEFAULT_SELECTION_RECTANGLE_FILL_COLOR;

    /**
     * Constructor
     */
    public MaanovaChartPanel() {
        this.chartRenderingInfo = new ChartRenderingInfo(null);
        this.renderChartImageTask = new RenderChartImageTask(this.chartRenderingInfo);
        this.renderChartImageTask.setSize(this.getWidth(), this.getHeight());

        this.addMouseListener(this.selfMouseListener);
        this.addMouseMotionListener(this.selfMotionListener);
        this.addComponentListener(this.selfComponentListener);

        Thread renderChartThread = new Thread(this.renderChartImageTask);
        renderChartThread.start();

        Thread getChartThread = new Thread(this.getNextChartRunnable);
        getChartThread.start();

        getProgressPanel().addTaskToTrack(this.renderChartImageTask);
    }

    /**
     * getter for the selection rectangle color
     * @return the selectionRectangleColor
     */
    public Color getSelectionRectangleColor() {
        return this.selectionRectangleColor;
    }

    /**
     * Get the fill color for the selection rectangle
     * @return the selectionRectangleFillColor
     */
    public Color getSelectionRectangleFillColor() {
        return this.selectionRectangleFillColor;
    }

    private static MultiTaskProgressPanel getProgressPanel() {
        return Maanova.getInstance().getApplicationFrame().getTaskProgressPanel();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void finalize() throws Throwable {
        getProgressPanel().removeTaskToTrack(this.renderChartImageTask);
    }

    private void selfResized() {
        this.renderChartImageTask.setSize(this.getWidth(), this.getHeight());
    }

    /**
     * Getter for the chart
     * @return the chart
     */
    public JFreeChart getChart() {
        return this.renderChartImageTask.getChart();
    }

    /**
     * Setter for the chart
     * @param chart the chart to set
     */
    public synchronized void setChart(JFreeChart chart) {
        this.renderChartImageTask.setChart(chart);
    }

    /**
     * Getter for the chart rendering info
     * @return the rendering info
     */
    public ChartRenderingInfo getChartRenderingInfo() {
        return this.chartRenderingInfo;
    }

    private void myMouseReleased(MouseEvent e) {
        if (this.dragRectangle != null && this.dragRectangle.width != 0 && this.dragRectangle.height != 0) {
            this.fireAreaSelectionEvent(this.dragRectangle);
        }

        this.dragRectangle = null;
        this.repaint();
    }

    private void myMouseDragged(MouseEvent e) {
        if (this.dragRectangle != null) {
            this.dragRectangle.width = e.getX() - this.dragRectangle.x;
            this.dragRectangle.height = e.getY() - this.dragRectangle.y;

            this.repaint();
        }
    }

    private void myMousePressed(MouseEvent e) {
        this.dragRectangle = new Rectangle(e.getX(), e.getY(), 0, 0);
    }

    /**
     * Adds an area selection listener
     * @param listener
     *          the listener
     */
    public void addAreaSelectionListener(AreaSelectionListener listener) {
        this.areaSelectionListeners.add(listener);
    }

    /**
     * Removes the given listener from the area selection listener list
     * @param listener
     *          the listener to remove
     */
    public void removeAreaSelectionListener(AreaSelectionListener listener) {
        this.areaSelectionListeners.remove(listener);
    }

    private void fireAreaSelectionEvent(Rectangle2D selectionRect) {
        Iterator<AreaSelectionListener> iter = this.areaSelectionListeners.iterator();
        while (iter.hasNext()) {
            iter.next().areaSelected(selectionRect);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        if (this.chartImage != null) {
            if (this.chartImage.getWidth() == this.getWidth() && this.chartImage.getHeight() == this.getHeight()) {
                g2.drawImage(this.chartImage, 0, 0, this);
            } else {
                g2.drawImage(this.chartImage, 0, 0, this.getWidth(), this.getHeight(), this.getBackground(), this);
            }
        }

        if (this.dragRectangle != null) {
            this.renderDragRectangle(g2);
        }
    }

    /**
     * Render the selection rectangle
     * @param graphics2D
     *          the graphics context to render to
     */
    protected synchronized void renderDragRectangle(Graphics2D graphics2D) {
        Rectangle2D nonNegativeSelectionRectangle = MaanovaChartPanel
                .toNonNegativeWidthHeightRectangle(this.dragRectangle);
        graphics2D.setColor(this.getSelectionRectangleFillColor());
        graphics2D.fill(nonNegativeSelectionRectangle);
        graphics2D.setColor(this.getSelectionRectangleColor());
        graphics2D.draw(nonNegativeSelectionRectangle);
    }

    /**
     * Convert a rectangle with negative width or height to one with
     * positive width and height
     * @param rectangle
     *          the rectangle that might have negatives
     * @return
     *          the positive version, or the same instance that was passed
     *          in if it's already positive
     */
    protected static Rectangle2D toNonNegativeWidthHeightRectangle(Rectangle2D rectangle) {
        if (rectangle.getWidth() < 0 || rectangle.getHeight() < 0) {
            final double x;
            final double y;
            final double width;
            final double height;

            if (rectangle.getWidth() < 0) {
                width = -rectangle.getWidth();
                x = rectangle.getX() + rectangle.getWidth();
            } else {
                width = rectangle.getWidth();
                x = rectangle.getX();
            }

            if (rectangle.getHeight() < 0) {
                height = -rectangle.getHeight();
                y = rectangle.getY() + rectangle.getHeight();
            } else {
                height = rectangle.getHeight();
                y = rectangle.getY();
            }

            return new Rectangle2D.Double(x, y, width, height);
        } else {
            // the rectangle that we have is OK
            return rectangle;
        }
    }

    /**
     * Convert the given point in java2d coordinates to chart coordinates.
     * @param java2DPoint the point to convert
     * @return the converted point
     * @throws ClassCastException the plot isn't an XYPlot
     */
    public Point2D toChartPoint(Point2D java2DPoint) throws ClassCastException {
        XYPlot plot = this.getChart().getXYPlot();
        Rectangle2D dataArea = this.chartRenderingInfo.getPlotInfo().getDataArea();

        double graphX = plot.getDomainAxis().java2DToValue(java2DPoint.getX(), dataArea, plot.getDomainAxisEdge());
        double graphY = plot.getRangeAxis().java2DToValue(java2DPoint.getY(), dataArea, plot.getRangeAxisEdge());

        return new Point2D.Double(graphX, graphY);
    }

    /**
     * Convert the given rectangle in java2d coordinates to chart coordinates.
     * @param java2DRectangle the rectangle to convert
     * @return the converted rectangle
     * @throws ClassCastException the plot isn't an XYPlot
     */
    public Rectangle2D toChartRectangle(Rectangle2D java2DRectangle) throws ClassCastException {
        XYPlot plot = this.getChart().getXYPlot();
        Rectangle2D dataArea = this.chartRenderingInfo.getPlotInfo().getDataArea();

        double x1 = plot.getDomainAxis().java2DToValue(java2DRectangle.getMinX(), dataArea,
                plot.getDomainAxisEdge());
        double y1 = plot.getRangeAxis().java2DToValue(java2DRectangle.getMinY(), dataArea, plot.getRangeAxisEdge());
        double x2 = plot.getDomainAxis().java2DToValue(java2DRectangle.getMaxX(), dataArea,
                plot.getDomainAxisEdge());
        double y2 = plot.getRangeAxis().java2DToValue(java2DRectangle.getMaxY(), dataArea, plot.getRangeAxisEdge());

        return toNonNegativeWidthHeightRectangle(new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1));
    }
}