Java tutorial
/* * 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)); } }