package jyplot;

import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.EventListener;
import java.util.ResourceBundle;

import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.ToolTipManager;
import javax.swing.event.EventListenerList;

import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.editor.ChartEditor;
import org.jfree.chart.editor.ChartEditorManager;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.event.ChartProgressEvent;
import org.jfree.chart.event.ChartProgressListener;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.ValueAxisPlot;
import org.jfree.chart.plot.Zoomable;
import org.jfree.ui.ExtensionFileFilter;

/**
 * This has been modified so that other classes can extend it and modify the
 * mouse behaviour. It is just the ChartPanel code with "private" replaced by
 * "public". A Swing GUI component for displaying a {@link JFreeChart} object.
 * 

The panel registers with the chart to receive notification of changes to * any component of the chart. The chart is redrawn automatically whenever this * notification is received. */ public class BaseChartPanel extends JPanel implements ChartChangeListener, ChartProgressListener, ActionListener, MouseListener, MouseMotionListener, Printable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6046366297214274674L; /** Default setting for buffer usage. */ public static final boolean DEFAULT_BUFFER_USED = false; /** The default panel width. */ public static final int DEFAULT_WIDTH = 680; /** The default panel height. */ public static final int DEFAULT_HEIGHT = 420; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MINIMUM_DRAW_WIDTH = 300; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MINIMUM_DRAW_HEIGHT = 200; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MAXIMUM_DRAW_WIDTH = 800; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MAXIMUM_DRAW_HEIGHT = 600; /** The minimum size required to perform a zoom on a rectangle */ public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10; /** Properties action command. */ public static final String PROPERTIES_COMMAND = "PROPERTIES"; /** Save action command. */ public static final String SAVE_COMMAND = "SAVE"; /** Print action command. */ public static final String PRINT_COMMAND = "PRINT"; /** Zoom in (both axes) action command. */ public static final String ZOOM_IN_BOTH_COMMAND = "ZOOM_IN_BOTH"; /** Zoom in (domain axis only) action command. */ public static final String ZOOM_IN_DOMAIN_COMMAND = "ZOOM_IN_DOMAIN"; /** Zoom in (range axis only) action command. */ public static final String ZOOM_IN_RANGE_COMMAND = "ZOOM_IN_RANGE"; /** Zoom out (both axes) action command. */ public static final String ZOOM_OUT_BOTH_COMMAND = "ZOOM_OUT_BOTH"; /** Zoom out (domain axis only) action command. */ public static final String ZOOM_OUT_DOMAIN_COMMAND = "ZOOM_DOMAIN_BOTH"; /** Zoom out (range axis only) action command. */ public static final String ZOOM_OUT_RANGE_COMMAND = "ZOOM_RANGE_BOTH"; /** Zoom reset (both axes) action command. */ public static final String ZOOM_RESET_BOTH_COMMAND = "ZOOM_RESET_BOTH"; /** Zoom reset (domain axis only) action command. */ public static final String ZOOM_RESET_DOMAIN_COMMAND = "ZOOM_RESET_DOMAIN"; /** Zoom reset (range axis only) action command. */ public static final String ZOOM_RESET_RANGE_COMMAND = "ZOOM_RESET_RANGE"; /** The chart that is displayed in the panel. */ private JFreeChart chart; /** Storage for registered (chart) mouse listeners. */ private EventListenerList chartMouseListeners; /** A flag that controls whether or not the off-screen buffer is used. */ private boolean useBuffer; /** A flag that indicates that the buffer should be refreshed. */ private boolean refreshBuffer; /** A buffer for the rendered chart. */ private Image chartBuffer; /** The height of the chart buffer. */ private int chartBufferHeight; /** The width of the chart buffer. */ private int chartBufferWidth; /** * The minimum width for drawing a chart (uses scaling for smaller widths). */ private int minimumDrawWidth; /** * The minimum height for drawing a chart (uses scaling for smaller * heights). */ private int minimumDrawHeight; /** * The maximum width for drawing a chart (uses scaling for bigger widths). */ private int maximumDrawWidth; /** * The maximum height for drawing a chart (uses scaling for bigger heights). */ private int maximumDrawHeight; /** The popup menu for the frame. */ private JPopupMenu popup; /** The drawing info collected the last time the chart was drawn. */ private ChartRenderingInfo info; /** The chart anchor point. */ private Point2D anchor; /** The scale factor used to draw the chart. */ private double scaleX; /** The scale factor used to draw the chart. */ private double scaleY; /** The plot orientation. */ private PlotOrientation orientation = PlotOrientation.VERTICAL; /** A flag that controls whether or not domain zooming is enabled. */ private boolean domainZoomable = false; /** A flag that controls whether or not range zooming is enabled. */ private boolean rangeZoomable = false; /** * The zoom rectangle starting point (selected by the user with a mouse * click). This is a point on the screen, not the chart (which may have been * scaled up or down to fit the panel). */ private Point zoomPoint = null; /** The zoom rectangle (selected by the user with the mouse). */ private transient Rectangle2D zoomRectangle = null; /** Controls if the zoom rectangle is drawn as an outline or filled. */ private boolean fillZoomRectangle = false; /** The minimum distance required to drag the mouse to trigger a zoom. */ private int zoomTriggerDistance; /** A flag that controls whether or not horizontal tracing is enabled. */ private boolean horizontalAxisTrace = false; /** A flag that controls whether or not vertical tracing is enabled. */ private boolean verticalAxisTrace = false; /** A vertical trace line. */ private transient Line2D verticalTraceLine; /** A horizontal trace line. */ private transient Line2D horizontalTraceLine; /** Menu item for zooming in on a chart (both axes). */ private JMenuItem zoomInBothMenuItem; /** Menu item for zooming in on a chart (domain axis). */ private JMenuItem zoomInDomainMenuItem; /** Menu item for zooming in on a chart (range axis). */ private JMenuItem zoomInRangeMenuItem; /** Menu item for zooming out on a chart. */ private JMenuItem zoomOutBothMenuItem; /** Menu item for zooming out on a chart (domain axis). */ private JMenuItem zoomOutDomainMenuItem; /** Menu item for zooming out on a chart (range axis). */ private JMenuItem zoomOutRangeMenuItem; /** Menu item for resetting the zoom (both axes). */ private JMenuItem zoomResetBothMenuItem; /** Menu item for resetting the zoom (domain axis only). */ private JMenuItem zoomResetDomainMenuItem; /** Menu item for resetting the zoom (range axis only). */ private JMenuItem zoomResetRangeMenuItem; /** A flag that controls whether or not file extensions are enforced. */ private boolean enforceFileExtensions; /** A flag that indicates if original tooltip delays are changed. */ private boolean ownToolTipDelaysActive; /** Original initial tooltip delay of ToolTipManager.sharedInstance(). */ private int originalToolTipInitialDelay; /** Original reshow tooltip delay of ToolTipManager.sharedInstance(). */ private int originalToolTipReshowDelay; /** Original dismiss tooltip delay of ToolTipManager.sharedInstance(). */ private int originalToolTipDismissDelay; /** Own initial tooltip delay to be used in this chart panel. */ private int ownToolTipInitialDelay; /** Own reshow tooltip delay to be used in this chart panel. */ private int ownToolTipReshowDelay; /** Own dismiss tooltip delay to be used in this chart panel. */ private int ownToolTipDismissDelay; /** The factor used to zoom in on an axis range. */ private double zoomInFactor = 0.5; /** The factor used to zoom out on an axis range. */ private double zoomOutFactor = 2.0; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundle.getBundle("org.jfree.chart.LocalizationBundle"); /** * Constructs a panel that displays the specified chart. * * @param chart * the chart. */ public BaseChartPanel(JFreeChart chart) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, DEFAULT_BUFFER_USED, true, // properties true, // save true, // print true, // zoom true // tooltips ); } /** * Constructs a panel containing a chart. * * @param chart * the chart. * @param useBuffer * a flag controlling whether or not an off-screen buffer is * used. */ public BaseChartPanel(JFreeChart chart, boolean useBuffer) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, useBuffer, true, // properties true, // save true, // print true, // zoom true // tooltips ); } /** * Constructs a JFreeChart panel. * * @param chart * the chart. * @param properties * a flag indicating whether or not the chart property editor * should be available via the popup menu. * @param save * a flag indicating whether or not save options should be * available via the popup menu. * @param print * a flag indicating whether or not the print option should be * available via the popup menu. * @param zoom * a flag indicating whether or not zoom options should be added * to the popup menu. * @param tooltips * a flag indicating whether or not tooltips should be enabled * for the chart. */ public BaseChartPanel(JFreeChart chart, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, DEFAULT_BUFFER_USED, properties, save, print, zoom, tooltips); } /** * Constructs a JFreeChart panel. * * @param chart * the chart. * @param width * the preferred width of the panel. * @param height * the preferred height of the panel. * @param minimumDrawWidth * the minimum drawing width. * @param minimumDrawHeight * the minimum drawing height. * @param maximumDrawWidth * the maximum drawing width. * @param maximumDrawHeight * the maximum drawing height. * @param useBuffer * a flag that indicates whether to use the off-screen buffer to * improve performance (at the expense of memory). * @param properties * a flag indicating whether or not the chart property editor * should be available via the popup menu. * @param save * a flag indicating whether or not save options should be * available via the popup menu. * @param print * a flag indicating whether or not the print option should be * available via the popup menu. * @param zoom * a flag indicating whether or not zoom options should be added * to the popup menu. * @param tooltips * a flag indicating whether or not tooltips should be enabled * for the chart. */ public BaseChartPanel(JFreeChart chart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean useBuffer, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) { this.chart = chart; this.chartMouseListeners = new EventListenerList(); if(chart != null) { chart.addChangeListener(this); Plot plot = chart.getPlot(); this.domainZoomable = false; this.rangeZoomable = false; if(plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = z.isDomainZoomable(); this.rangeZoomable = z.isRangeZoomable(); this.orientation = z.getOrientation(); } } this.info = new ChartRenderingInfo(); setPreferredSize(new Dimension(width, height)); this.useBuffer = useBuffer; this.refreshBuffer = false; this.minimumDrawWidth = minimumDrawWidth; this.minimumDrawHeight = minimumDrawHeight; this.maximumDrawWidth = maximumDrawWidth; this.maximumDrawHeight = maximumDrawHeight; this.zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE; // set up popup menu... this.popup = null; if(properties || save || print || zoom) { this.popup = createPopupMenu(properties, save, print, zoom); } enableEvents(AWTEvent.MOUSE_EVENT_MASK); enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); setDisplayToolTips(tooltips); addMouseListener(this); addMouseMotionListener(this); this.enforceFileExtensions = true; // initialize BaseChartPanel-specific tool tip delays with // values the from ToolTipManager.sharedInstance() ToolTipManager ttm = ToolTipManager.sharedInstance(); this.ownToolTipInitialDelay = ttm.getInitialDelay(); this.ownToolTipDismissDelay = ttm.getDismissDelay(); this.ownToolTipReshowDelay = ttm.getReshowDelay(); } /** * Returns the chart contained in the panel. * * @return The chart (possibly null). */ public JFreeChart getChart() { return this.chart; } /** * Sets the chart that is displayed in the panel. * * @param chart * the chart (null permitted). */ public void setChart(JFreeChart chart) { // stop listening for changes to the existing chart if(this.chart != null) { this.chart.removeChangeListener(this); this.chart.removeProgressListener(this); } // add the new chart this.chart = chart; if(chart != null) { this.chart.addChangeListener(this); this.chart.addProgressListener(this); Plot plot = chart.getPlot(); this.domainZoomable = false; this.rangeZoomable = false; if(plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = z.isDomainZoomable(); this.rangeZoomable = z.isRangeZoomable(); this.orientation = z.getOrientation(); } } else { this.domainZoomable = false; this.rangeZoomable = false; } if(this.useBuffer) { this.refreshBuffer = true; } repaint(); } /** * Returns the minimum drawing width for charts.

If the width available * on the panel is less than this, then the chart is drawn at the minimum * width then scaled down to fit. * * @return The minimum drawing width. */ public int getMinimumDrawWidth() { return this.minimumDrawWidth; } /** * Sets the minimum drawing width for the chart on this panel.

At the * time the chart is drawn on the panel, if the available width is less than * this amount, the chart will be drawn using the minimum width then scaled * down to fit the available space. * * @param width * The width. */ public void setMinimumDrawWidth(int width) { this.minimumDrawWidth = width; } /** * Returns the maximum drawing width for charts.

If the width available * on the panel is greater than this, then the chart is drawn at the maximum * width then scaled up to fit. * * @return The maximum drawing width. */ public int getMaximumDrawWidth() { return this.maximumDrawWidth; } /** * Sets the maximum drawing width for the chart on this panel.

At the * time the chart is drawn on the panel, if the available width is greater * than this amount, the chart will be drawn using the maximum width then * scaled up to fit the available space. * * @param width * The width. */ public void setMaximumDrawWidth(int width) { this.maximumDrawWidth = width; } /** * Returns the minimum drawing height for charts.

If the height * available on the panel is less than this, then the chart is drawn at the * minimum height then scaled down to fit. * * @return The minimum drawing height. */ public int getMinimumDrawHeight() { return this.minimumDrawHeight; } /** * Sets the minimum drawing height for the chart on this panel.

At the * time the chart is drawn on the panel, if the available height is less * than this amount, the chart will be drawn using the minimum height then * scaled down to fit the available space. * * @param height * The height. */ public void setMinimumDrawHeight(int height) { this.minimumDrawHeight = height; } /** * Returns the maximum drawing height for charts.

If the height * available on the panel is greater than this, then the chart is drawn at * the maximum height then scaled up to fit. * * @return The maximum drawing height. */ public int getMaximumDrawHeight() { return this.maximumDrawHeight; } /** * Sets the maximum drawing height for the chart on this panel.

At the * time the chart is drawn on the panel, if the available height is greater * than this amount, the chart will be drawn using the maximum height then * scaled up to fit the available space. * * @param height * The height. */ public void setMaximumDrawHeight(int height) { this.maximumDrawHeight = height; } /** * Returns the X scale factor for the chart. This will be 1.0 if no scaling * has been used. * * @return The scale factor. */ public double getScaleX() { return this.scaleX; } /** * Returns the Y scale factory for the chart. This will be 1.0 if no scaling * has been used. * * @return The scale factor. */ public double getScaleY() { return this.scaleY; } /** * Returns the anchor point. * * @return The anchor point (possibly null). */ public Point2D getAnchor() { return this.anchor; } /** * Sets the anchor point. This method is provided for the use of subclasses, * not end users. * * @param anchor * the anchor point (null permitted). */ protected void setAnchor(Point2D anchor) { this.anchor = anchor; } /** * Returns the popup menu. * * @return The popup menu. */ public JPopupMenu getPopupMenu() { return this.popup; } /** * Sets the popup menu for the panel. * * @param popup * the popup menu (null permitted). */ public void setPopupMenu(JPopupMenu popup) { this.popup = popup; } /** * Returns the chart rendering info from the most recent chart redraw. * * @return The chart rendering info. */ public ChartRenderingInfo getChartRenderingInfo() { return this.info; } /** * A convenience method that switches on mouse-based zooming. * * @param flag * true enables zooming and rectangle fill on * zoom. */ public void setMouseZoomable(boolean flag) { setMouseZoomable(flag, true); } /** * A convenience method that switches on mouse-based zooming. * * @param flag * true if zooming enabled * @param fillRectangle * true if zoom rectangle is filled, false if * rectangle is shown as outline only. */ public void setMouseZoomable(boolean flag, boolean fillRectangle) { setDomainZoomable(flag); setRangeZoomable(flag); setFillZoomRectangle(fillRectangle); } /** * Returns the flag that determines whether or not zooming is enabled for * the domain axis. * * @return A boolean. */ public boolean isDomainZoomable() { return this.domainZoomable; } /** * Sets the flag that controls whether or not zooming is enable for the * domain axis. A check is made to ensure that the current plot supports * zooming for the domain values. * * @param flag * true enables zooming if possible. */ public void setDomainZoomable(boolean flag) { if(flag) { Plot plot = this.chart.getPlot(); if(plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = flag && (z.isDomainZoomable()); } } else { this.domainZoomable = false; } } /** * Returns the flag that determines whether or not zooming is enabled for * the range axis. * * @return A boolean. */ public boolean isRangeZoomable() { return this.rangeZoomable; } /** * A flag that controls mouse-based zooming on the vertical axis. * * @param flag * true enables zooming. */ public void setRangeZoomable(boolean flag) { if(flag) { Plot plot = this.chart.getPlot(); if(plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.rangeZoomable = flag && (z.isRangeZoomable()); } } else { this.rangeZoomable = false; } } /** * Returns the flag that controls whether or not the zoom rectangle is * filled when drawn. * * @return A boolean. */ public boolean getFillZoomRectangle() { return this.fillZoomRectangle; } /** * A flag that controls how the zoom rectangle is drawn. * * @param flag * true instructs to fill the rectangle on zoom, * otherwise it will be outlined. */ public void setFillZoomRectangle(boolean flag) { this.fillZoomRectangle = flag; } /** * Returns the zoom trigger distance. This controls how far the mouse must * move before a zoom action is triggered. * * @return The distance (in Java2D units). */ public int getZoomTriggerDistance() { return this.zoomTriggerDistance; } /** * Sets the zoom trigger distance. This controls how far the mouse must move * before a zoom action is triggered. * * @param distance * the distance (in Java2D units). */ public void setZoomTriggerDistance(int distance) { this.zoomTriggerDistance = distance; } /** * Returns the flag that controls whether or not a horizontal axis trace * line is drawn over the plot area at the current mouse location. * * @return A boolean. */ public boolean getHorizontalAxisTrace() { return this.horizontalAxisTrace; } /** * A flag that controls trace lines on the horizontal axis. * * @param flag * true enables trace lines for the mouse pointer * on the horizontal axis. */ public void setHorizontalAxisTrace(boolean flag) { this.horizontalAxisTrace = flag; } /** * Returns the horizontal trace line. * * @return The horizontal trace line (possibly null). */ protected Line2D getHorizontalTraceLine() { return this.horizontalTraceLine; } /** * Sets the horizontal trace line. * * @param line * the line (null permitted). */ protected void setHorizontalTraceLine(Line2D line) { this.horizontalTraceLine = line; } /** * Returns the flag that controls whether or not a vertical axis trace line * is drawn over the plot area at the current mouse location. * * @return A boolean. */ public boolean getVerticalAxisTrace() { return this.verticalAxisTrace; } /** * A flag that controls trace lines on the vertical axis. * * @param flag * true enables trace lines for the mouse pointer * on the vertical axis. */ public void setVerticalAxisTrace(boolean flag) { this.verticalAxisTrace = flag; } /** * Returns the vertical trace line. * * @return The vertical trace line (possibly null). */ protected Line2D getVerticalTraceLine() { return this.verticalTraceLine; } /** * Sets the vertical trace line. * * @param line * the line (null permitted). */ protected void setVerticalTraceLine(Line2D line) { this.verticalTraceLine = line; } /** * Returns true if file extensions should be enforced, and * false otherwise. * * @return The flag. */ public boolean isEnforceFileExtensions() { return this.enforceFileExtensions; } /** * Sets a flag that controls whether or not file extensions are enforced. * * @param enforce * the new flag value. */ public void setEnforceFileExtensions(boolean enforce) { this.enforceFileExtensions = enforce; } /** * Switches the display of tooltips for the panel on or off. Note that * tooltips can only be displayed if the chart has been configured to * generate tooltip items. * * @param flag * true to enable tooltips, false * to disable tooltips. */ public void setDisplayToolTips(boolean flag) { if(flag) { ToolTipManager.sharedInstance().registerComponent(this); } else { ToolTipManager.sharedInstance().unregisterComponent(this); } } /** * Returns a string for the tooltip. * * @param e * the mouse event. * @return A tool tip or null if no tooltip is available. */ public String getToolTipText(MouseEvent e) { String result = null; if(this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if(entities != null) { Insets insets = getInsets(); ChartEntity entity = entities.getEntity((int) ((e.getX() - insets.left) / this.scaleX), (int) ((e.getY() - insets.top) / this.scaleY)); if(entity != null) { result = entity.getToolTipText(); } } } return result; } /** * Translates a Java2D point on the chart to a screen location. * * @param java2DPoint * the Java2D point. * @return The screen location. */ public Point translateJava2DToScreen(Point2D java2DPoint) { Insets insets = getInsets(); int x = (int) (java2DPoint.getX() * this.scaleX + insets.left); int y = (int) (java2DPoint.getY() * this.scaleY + insets.top); return new Point(x, y); } /** * Translates a screen location to a Java2D point. * * @param screenPoint * the screen location. * @return The Java2D coordinates. */ public Point2D translateScreenToJava2D(Point screenPoint) { Insets insets = getInsets(); double x = (screenPoint.getX() - insets.left) / this.scaleX; double y = (screenPoint.getY() - insets.top) / this.scaleY; return new Point2D.Double(x, y); } /** * Applies any scaling that is in effect for the chart drawing to the given * rectangle. * * @param rect * the rectangle. * @return A new scaled rectangle. */ public Rectangle2D scale(Rectangle2D rect) { Insets insets = getInsets(); double x = rect.getX() * getScaleX() + insets.left; double y = rect.getY() * this.getScaleY() + insets.top; double w = rect.getWidth() * this.getScaleX(); double h = rect.getHeight() * this.getScaleY(); return new Rectangle2D.Double(x, y, w, h); } /** * Returns the chart entity at a given point.

This method will return * null if there is (a) no entity at the given point, or (b) no entity * collection has been generated. * * @param viewX * the x-coordinate. * @param viewY * the y-coordinate. * @return The chart entity (possibly null). */ public ChartEntity getEntityForPoint(int viewX, int viewY) { ChartEntity result = null; if(this.info != null) { Insets insets = getInsets(); double x = (viewX - insets.left) / this.scaleX; double y = (viewY - insets.top) / this.scaleY; EntityCollection entities = this.info.getEntityCollection(); result = entities != null ? entities.getEntity(x, y) : null; } return result; } /** * Returns the flag that controls whether or not the offscreen buffer needs * to be refreshed. * * @return A boolean. */ public boolean getRefreshBuffer() { return this.refreshBuffer; } /** * Sets the refresh buffer flag. This flag is used to avoid unnecessary * redrawing of the chart when the offscreen image buffer is used. * * @param flag * true indicate, that the buffer should be * refreshed. */ public void setRefreshBuffer(boolean flag) { this.refreshBuffer = flag; } /** * Paints the component by drawing the chart to fill the entire component, * but allowing for the insets (which will be non-zero if a border has been * set for this component). To increase performance (at the expense of * memory), an off-screen buffer image can be used. * * @param g * the graphics device for drawing on. */ public void paintComponent(Graphics g) { super.paintComponent(g); if(this.chart == null) { return; } Graphics2D g2 = (Graphics2D) g.create(); // first determine the size of the chart rendering area... Dimension size = getSize(); Insets insets = getInsets(); Rectangle2D available = new Rectangle2D.Double(insets.left, insets.top, size.getWidth() - insets.left - insets.right, size.getHeight() - insets.top - insets.bottom); // work out if scaling is required... boolean scale = false; double drawWidth = available.getWidth(); double drawHeight = available.getHeight(); this.scaleX = 1.0; this.scaleY = 1.0; if(drawWidth < this.minimumDrawWidth) { this.scaleX = drawWidth / this.minimumDrawWidth; drawWidth = this.minimumDrawWidth; scale = true; } else if(drawWidth > this.maximumDrawWidth) { this.scaleX = drawWidth / this.maximumDrawWidth; drawWidth = this.maximumDrawWidth; scale = true; } if(drawHeight < this.minimumDrawHeight) { this.scaleY = drawHeight / this.minimumDrawHeight; drawHeight = this.minimumDrawHeight; scale = true; } else if(drawHeight > this.maximumDrawHeight) { this.scaleY = drawHeight / this.maximumDrawHeight; drawHeight = this.maximumDrawHeight; scale = true; } Rectangle2D chartArea = new Rectangle2D.Double(0.0, 0.0, drawWidth, drawHeight); // are we using the chart buffer? if(this.useBuffer) { // do we need to resize the buffer? if((this.chartBuffer == null) || (this.chartBufferWidth != available.getWidth()) || (this.chartBufferHeight != available.getHeight())) { this.chartBufferWidth = (int) available.getWidth(); this.chartBufferHeight = (int) available.getHeight(); this.chartBuffer = createImage(this.chartBufferWidth, this.chartBufferHeight); this.refreshBuffer = true; } // do we need to redraw the buffer? if(this.refreshBuffer) { Rectangle2D bufferArea = new Rectangle2D.Double(0, 0, this.chartBufferWidth, this.chartBufferHeight); Graphics2D bufferG2 = (Graphics2D) this.chartBuffer.getGraphics(); if(scale) { AffineTransform saved = bufferG2.getTransform(); AffineTransform st = AffineTransform.getScaleInstance(this.scaleX, this.scaleY); bufferG2.transform(st); this.chart.draw(bufferG2, chartArea, this.anchor, this.info); bufferG2.setTransform(saved); } else { this.chart.draw(bufferG2, bufferArea, this.anchor, this.info); } this.refreshBuffer = false; } // zap the buffer onto the panel... g2.drawImage(this.chartBuffer, insets.left, insets.right, this); } // or redrawing the chart every time... else { AffineTransform saved = g2.getTransform(); g2.translate(insets.left, insets.top); if(scale) { AffineTransform st = AffineTransform.getScaleInstance(this.scaleX, this.scaleY); g2.transform(st); } this.chart.draw(g2, chartArea, this.anchor, this.info); g2.setTransform(saved); } this.anchor = null; this.verticalTraceLine = null; this.horizontalTraceLine = null; } /** * Receives notification of changes to the chart, and redraws the chart. * * @param event * details of the chart change event. */ public void chartChanged(ChartChangeEvent event) { this.refreshBuffer = true; Plot plot = chart.getPlot(); if(plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.orientation = z.getOrientation(); } repaint(); } /** * Receives notification of a chart progress event. * * @param event * the event. */ public void chartProgress(ChartProgressEvent event) { // does nothing - override if necessary } /** * Handles action events generated by the popup menu. * * @param event * the event. */ public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if(command.equals(PROPERTIES_COMMAND)) { attemptEditChartProperties(); } else if(command.equals(SAVE_COMMAND)) { try { doSaveAs(); } catch (IOException e) { e.printStackTrace(); } } else if(command.equals(PRINT_COMMAND)) { createChartPrintJob(); } else if(command.equals(ZOOM_IN_BOTH_COMMAND)) { zoomInBoth(this.zoomPoint.getX(), this.zoomPoint.getY()); } else if(command.equals(ZOOM_IN_DOMAIN_COMMAND)) { zoomInDomain(this.zoomPoint.getX(), this.zoomPoint.getY()); } else if(command.equals(ZOOM_IN_RANGE_COMMAND)) { zoomInRange(this.zoomPoint.getX(), this.zoomPoint.getY()); } else if(command.equals(ZOOM_OUT_BOTH_COMMAND)) { zoomOutBoth(this.zoomPoint.getX(), this.zoomPoint.getY()); } else if(command.equals(ZOOM_OUT_DOMAIN_COMMAND)) { zoomOutDomain(this.zoomPoint.getX(), this.zoomPoint.getY()); } else if(command.equals(ZOOM_OUT_RANGE_COMMAND)) { zoomOutRange(this.zoomPoint.getX(), this.zoomPoint.getY()); } else if(command.equals(ZOOM_RESET_BOTH_COMMAND)) { restoreAutoBounds(); } else if(command.equals(ZOOM_RESET_DOMAIN_COMMAND)) { restoreAutoDomainBounds(); } else if(command.equals(ZOOM_RESET_RANGE_COMMAND)) { restoreAutoRangeBounds(); } } /** * Handles a 'mouse entered' event. This method changes the tooltip delays * of ToolTipManager.sharedInstance() to the possibly different values set * for this chart panel. * * @param e * the mouse event. */ public void mouseEntered(MouseEvent e) { if(!this.ownToolTipDelaysActive) { ToolTipManager ttm = ToolTipManager.sharedInstance(); this.originalToolTipInitialDelay = ttm.getInitialDelay(); ttm.setInitialDelay(this.ownToolTipInitialDelay); this.originalToolTipReshowDelay = ttm.getReshowDelay(); ttm.setReshowDelay(this.ownToolTipReshowDelay); this.originalToolTipDismissDelay = ttm.getDismissDelay(); ttm.setDismissDelay(this.ownToolTipDismissDelay); this.ownToolTipDelaysActive = true; } } /** * Handles a 'mouse exited' event. This method resets the tooltip delays of * ToolTipManager.sharedInstance() to their original values in effect before * mouseEntered() * * @param e * the mouse event. */ public void mouseExited(MouseEvent e) { if(this.ownToolTipDelaysActive) { // restore original tooltip dealys ToolTipManager ttm = ToolTipManager.sharedInstance(); ttm.setInitialDelay(this.originalToolTipInitialDelay); ttm.setReshowDelay(this.originalToolTipReshowDelay); ttm.setDismissDelay(this.originalToolTipDismissDelay); this.ownToolTipDelaysActive = false; } } /** * Handles a 'mouse pressed' event.

This event is the popup trigger on * Unix/Linux. For Windows, the popup trigger is the 'mouse released' event. * * @param e * The mouse event. */ public void mousePressed(MouseEvent e) { if(this.zoomRectangle == null) { Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY()); if(screenDataArea != null) { this.zoomPoint = getPointInRectangle(e.getX(), e.getY(), screenDataArea); } else { this.zoomPoint = null; } if(e.isPopupTrigger()) { if(this.popup != null) { displayPopupMenu(e.getX(), e.getY()); } } } } /** * Returns a point based on (x, y) but constrained to be within the bounds * of the given rectangle. This method could be moved to JCommon. * * @param x * the x-coordinate. * @param y * the y-coordinate. * @param area * the rectangle (null not permitted). * @return A point within the rectangle. */ private Point getPointInRectangle(int x, int y, Rectangle2D area) { x = (int) Math.max(Math.ceil(area.getMinX()), Math.min(x, Math.floor(area.getMaxX()))); y = (int) Math.max(Math.ceil(area.getMinY()), Math.min(y, Math.floor(area.getMaxY()))); return new Point(x, y); } /** * Handles a 'mouse dragged' event. * * @param e * the mouse event. */ public void mouseDragged(MouseEvent e) { // if the popup menu has already been triggered, then ignore dragging... if(this.popup != null && this.popup.isShowing()) { return; } // if no initial zoom point was set, ignore dragging... if(this.zoomPoint == null) { return; } Graphics2D g2 = (Graphics2D) getGraphics(); // use XOR to erase the previous zoom rectangle (if any)... g2.setXORMode(java.awt.Color.gray); if(this.zoomRectangle != null) { if(this.fillZoomRectangle) { g2.fill(this.zoomRectangle); } else { g2.draw(this.zoomRectangle); } } boolean hZoom = false; boolean vZoom = false; if(this.orientation == PlotOrientation.HORIZONTAL) { hZoom = this.rangeZoomable; vZoom = this.domainZoomable; } else { hZoom = this.domainZoomable; vZoom = this.rangeZoomable; } Rectangle2D scaledDataArea = getScreenDataArea((int) this.zoomPoint.getX(), (int) this.zoomPoint.getY()); if(hZoom && vZoom) { // selected rectangle shouldn't extend outside the data area... double xmax = Math.min(e.getX(), scaledDataArea.getMaxX()); double ymax = Math.min(e.getY(), scaledDataArea.getMaxY()); this.zoomRectangle = new Rectangle2D.Double(this.zoomPoint.getX(), this.zoomPoint.getY(), xmax - this.zoomPoint.getX(), ymax - this.zoomPoint.getY()); } else if(hZoom) { double xmax = Math.min(e.getX(), scaledDataArea.getMaxX()); this.zoomRectangle = new Rectangle2D.Double(this.zoomPoint.getX(), scaledDataArea.getMinY(), xmax - this.zoomPoint.getX(), scaledDataArea.getHeight()); } else if(vZoom) { double ymax = Math.min(e.getY(), scaledDataArea.getMaxY()); this.zoomRectangle = new Rectangle2D.Double(scaledDataArea.getMinX(), this.zoomPoint.getY(), scaledDataArea.getWidth(), ymax - this.zoomPoint.getY()); } if(this.zoomRectangle != null) { // use XOR to draw the new zoom rectangle... if(this.fillZoomRectangle) { g2.fill(this.zoomRectangle); } else { g2.draw(this.zoomRectangle); } } g2.dispose(); } public void calulateSelectionRectangle() { } /** * Handles a 'mouse released' event. On Windows, we need to check if this is * a popup trigger, but only if we haven't already been tracking a zoom * rectangle. * * @param e * information about the event. */ public void mouseReleased(MouseEvent e) { if(this.zoomRectangle != null) { boolean hZoom = false; boolean vZoom = false; if(this.orientation == PlotOrientation.HORIZONTAL) { hZoom = this.rangeZoomable; vZoom = this.domainZoomable; } else { hZoom = this.domainZoomable; vZoom = this.rangeZoomable; } boolean zoomTrigger1 = hZoom && Math.abs(e.getX() - this.zoomPoint.getX()) >= this.zoomTriggerDistance; boolean zoomTrigger2 = vZoom && Math.abs(e.getY() - this.zoomPoint.getY()) >= this.zoomTriggerDistance; if(zoomTrigger1 || zoomTrigger2) { if((hZoom && (e.getX() < this.zoomPoint.getX())) || (vZoom && (e.getY() < this.zoomPoint.getY()))) { restoreAutoBounds(); } else { double x, y, w, h; Rectangle2D screenDataArea = getScreenDataArea((int) this.zoomPoint.getX(), (int) this.zoomPoint.getY()); // for mouseReleased event, (horizontalZoom || verticalZoom) // will be true, so we can just test for either being false; // otherwise both are true if(!vZoom) { x = this.zoomPoint.getX(); y = screenDataArea.getMinY(); w = Math.min(this.zoomRectangle.getWidth(), screenDataArea.getMaxX() - this.zoomPoint.getX()); h = screenDataArea.getHeight(); } else if(!hZoom) { x = screenDataArea.getMinX(); y = this.zoomPoint.getY(); w = screenDataArea.getWidth(); h = Math.min(this.zoomRectangle.getHeight(), screenDataArea.getMaxY() - this.zoomPoint.getY()); } else { x = this.zoomPoint.getX(); y = this.zoomPoint.getY(); w = Math.min(this.zoomRectangle.getWidth(), screenDataArea.getMaxX() - this.zoomPoint.getX()); h = Math.min(this.zoomRectangle.getHeight(), screenDataArea.getMaxY() - this.zoomPoint.getY()); } Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w, h); zoom(zoomArea); } this.zoomPoint = null; this.zoomRectangle = null; } else { Graphics2D g2 = (Graphics2D) getGraphics(); g2.setXORMode(java.awt.Color.gray); if(this.fillZoomRectangle) { g2.fill(this.zoomRectangle); } else { g2.draw(this.zoomRectangle); } g2.dispose(); this.zoomPoint = null; this.zoomRectangle = null; } } else if(e.isPopupTrigger()) { if(this.popup != null) { displayPopupMenu(e.getX(), e.getY()); } } } /** * Receives notification of mouse clicks on the panel. These are translated * and passed on to any registered chart mouse click listeners. * * @param event * Information about the mouse event. */ public void mouseClicked(MouseEvent event) { Insets insets = getInsets(); int x = (int) ((event.getX() - insets.left) / this.scaleX); int y = (int) ((event.getY() - insets.top) / this.scaleY); this.anchor = new Point2D.Double(x, y); this.chart.setNotify(true); // force a redraw // new entity code... Object[] listeners = this.chartMouseListeners.getListeners(ChartMouseListener.class); if(listeners.length == 0) { return; } ChartEntity entity = null; if(this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if(entities != null) { entity = entities.getEntity(x, y); } } ChartMouseEvent chartEvent = new ChartMouseEvent(getChart(), event, entity); for (int i = listeners.length - 1; i >= 0; i -= 1) { ((ChartMouseListener) listeners[i]).chartMouseClicked(chartEvent); } } /** * Implementation of the MouseMotionListener's method. * * @param e * the event. */ public void mouseMoved(MouseEvent e) { if(this.horizontalAxisTrace) { drawHorizontalAxisTrace(e.getX()); } if(this.verticalAxisTrace) { drawVerticalAxisTrace(e.getY()); } Object[] listeners = this.chartMouseListeners.getListeners(ChartMouseListener.class); if(listeners.length == 0) { return; } Insets insets = getInsets(); int x = (int) ((e.getX() - insets.left) / this.scaleX); int y = (int) ((e.getY() - insets.top) / this.scaleY); ChartEntity entity = null; if(this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if(entities != null) { entity = entities.getEntity(x, y); } } ChartMouseEvent event = new ChartMouseEvent(getChart(), e, entity); for (int i = listeners.length - 1; i >= 0; i -= 1) { ((ChartMouseListener) listeners[i]).chartMouseMoved(event); } } /** * Zooms in on an anchor point (specified in screen coordinate space). * * @param x * the x value (in screen coordinates). * @param y * the y value (in screen coordinates). */ public void zoomInBoth(double x, double y) { zoomInDomain(x, y); zoomInRange(x, y); } /** * Decreases the length of the domain axis, centered about the given * coordinate on the screen. The length of the domain axis is reduced by the * value of {@link #getZoomInFactor()}. * * @param x * the x coordinate (in screen coordinates). * @param y * the y-coordinate (in screen coordinates). */ public void zoomInDomain(double x, double y) { Plot p = this.chart.getPlot(); if(p instanceof Zoomable) { Zoomable plot = (Zoomable) p; plot.zoomDomainAxes(this.zoomInFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Decreases the length of the range axis, centered about the given * coordinate on the screen. The length of the range axis is reduced by the * value of {@link #getZoomInFactor()}. * * @param x * the x-coordinate (in screen coordinates). * @param y * the y coordinate (in screen coordinates). */ public void zoomInRange(double x, double y) { Plot p = this.chart.getPlot(); if(p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomRangeAxes(this.zoomInFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Zooms out on an anchor point (specified in screen coordinate space). * * @param x * the x value (in screen coordinates). * @param y * the y value (in screen coordinates). */ public void zoomOutBoth(double x, double y) { zoomOutDomain(x, y); zoomOutRange(x, y); } /** * Increases the length of the domain axis, centered about the given * coordinate on the screen. The length of the domain axis is increased by * the value of {@link #getZoomOutFactor()}. * * @param x * the x coordinate (in screen coordinates). * @param y * the y-coordinate (in screen coordinates). */ public void zoomOutDomain(double x, double y) { Plot p = this.chart.getPlot(); if(p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomDomainAxes(this.zoomOutFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Increases the length the range axis, centered about the given coordinate * on the screen. The length of the range axis is increased by the value of * {@link #getZoomOutFactor()}. * * @param x * the x coordinate (in screen coordinates). * @param y * the y-coordinate (in screen coordinates). */ public void zoomOutRange(double x, double y) { Plot p = this.chart.getPlot(); if(p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomRangeAxes(this.zoomOutFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Zooms in on a selected region. * * @param selection * the selected region. */ public void zoom(Rectangle2D selection) { // get the origin of the zoom selection in the Java2D space used for // drawing the chart (that is, before any scaling to fit the panel) Point2D selectOrigin = translateScreenToJava2D(new Point((int) Math.ceil(selection.getX()), (int) Math.ceil(selection.getY()))); PlotRenderingInfo plotInfo = this.info.getPlotInfo(); Rectangle2D scaledDataArea = getScreenDataArea((int) selection.getCenterX(), (int) selection.getCenterY()); if((selection.getHeight() > 0) && (selection.getWidth() > 0)) { double hLower = (selection.getMinX() - scaledDataArea.getMinX()) / scaledDataArea.getWidth(); double hUpper = (selection.getMaxX() - scaledDataArea.getMinX()) / scaledDataArea.getWidth(); double vLower = (scaledDataArea.getMaxY() - selection.getMaxY()) / scaledDataArea.getHeight(); double vUpper = (scaledDataArea.getMaxY() - selection.getMinY()) / scaledDataArea.getHeight(); Plot p = this.chart.getPlot(); if(p instanceof Zoomable) { Zoomable z = (Zoomable) p; if(z.getOrientation() == PlotOrientation.HORIZONTAL) { z.zoomDomainAxes(vLower, vUpper, plotInfo, selectOrigin); z.zoomRangeAxes(hLower, hUpper, plotInfo, selectOrigin); } else { z.zoomDomainAxes(hLower, hUpper, plotInfo, selectOrigin); z.zoomRangeAxes(vLower, vUpper, plotInfo, selectOrigin); } } } } /** * Restores the auto-range calculation on both axes. */ public void restoreAutoBounds() { restoreAutoDomainBounds(); restoreAutoRangeBounds(); } /** * Restores the auto-range calculation on the domain axis. */ public void restoreAutoDomainBounds() { Plot p = this.chart.getPlot(); if(p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomDomainAxes(0.0, this.info.getPlotInfo(), this.zoomPoint); } } /** * Restores the auto-range calculation on the range axis. */ public void restoreAutoRangeBounds() { Plot p = this.chart.getPlot(); if(p instanceof ValueAxisPlot) { Zoomable z = (Zoomable) p; z.zoomRangeAxes(0.0, this.info.getPlotInfo(), this.zoomPoint); } } /** * Returns the data area for the chart (the area inside the axes) with the * current scaling applied (that is, the area as it appears on screen). * * @return The scaled data area. */ public Rectangle2D getScreenDataArea() { Rectangle2D dataArea = this.info.getPlotInfo().getDataArea(); Insets insets = getInsets(); double x = dataArea.getX() * this.scaleX + insets.left; double y = dataArea.getY() * this.scaleY + insets.top; double w = dataArea.getWidth() * this.scaleX; double h = dataArea.getHeight() * this.scaleY; return new Rectangle2D.Double(x, y, w, h); } /** * Returns the data area (the area inside the axes) for the plot or subplot, * with the current scaling applied. * * @param x * the x-coordinate (for subplot selection). * @param y * the y-coordinate (for subplot selection). * @return The scaled data area. */ public Rectangle2D getScreenDataArea(int x, int y) { PlotRenderingInfo plotInfo = this.info.getPlotInfo(); Rectangle2D result; if(plotInfo.getSubplotCount() == 0) { result = getScreenDataArea(); } else { // get the origin of the zoom selection in the Java2D space used for // drawing the chart (that is, before any scaling to fit the panel) Point2D selectOrigin = translateScreenToJava2D(new Point(x, y)); int subplotIndex = plotInfo.getSubplotIndex(selectOrigin); if(subplotIndex == -1) { return null; } result = scale(plotInfo.getSubplotInfo(subplotIndex).getDataArea()); } return result; } /** * Returns the initial tooltip delay value used inside this chart panel. * * @return An integer representing the initial delay value, in milliseconds. * @see javax.swing.ToolTipManager#getInitialDelay() */ public int getInitialDelay() { return this.ownToolTipInitialDelay; } /** * Returns the reshow tooltip delay value used inside this chart panel. * * @return An integer representing the reshow delay value, in milliseconds. * @see javax.swing.ToolTipManager#getReshowDelay() */ public int getReshowDelay() { return this.ownToolTipReshowDelay; } /** * Returns the dismissal tooltip delay value used inside this chart panel. * * @return An integer representing the dismissal delay value, in * milliseconds. * @see javax.swing.ToolTipManager#getDismissDelay() */ public int getDismissDelay() { return this.ownToolTipDismissDelay; } /** * Specifies the initial delay value for this chart panel. * * @param delay * the number of milliseconds to delay (after the cursor has * paused) before displaying. * @see javax.swing.ToolTipManager#setInitialDelay(int) */ public void setInitialDelay(int delay) { this.ownToolTipInitialDelay = delay; } /** * Specifies the amount of time before the user has to wait initialDelay * milliseconds before a tooltip will be shown. * * @param delay * time in milliseconds * @see javax.swing.ToolTipManager#setReshowDelay(int) */ public void setReshowDelay(int delay) { this.ownToolTipReshowDelay = delay; } /** * Specifies the dismissal delay value for this chart panel. * * @param delay * the number of milliseconds to delay before taking away the * tooltip * @see javax.swing.ToolTipManager#setDismissDelay(int) */ public void setDismissDelay(int delay) { this.ownToolTipDismissDelay = delay; } /** * Returns the zoom in factor. * * @return The zoom in factor. */ public double getZoomInFactor() { return this.zoomInFactor; } /** * Sets the zoom in factor. * * @param factor * the factor. */ public void setZoomInFactor(double factor) { this.zoomInFactor = factor; } /** * Returns the zoom out factor. * * @return The zoom out factor. */ public double getZoomOutFactor() { return this.zoomOutFactor; } /** * Sets the zoom out factor. * * @param factor * the factor. */ public void setZoomOutFactor(double factor) { this.zoomOutFactor = factor; } /** * Draws a vertical line used to trace the mouse position to the horizontal * axis. * * @param x * the x-coordinate of the trace line. */ private void drawHorizontalAxisTrace(int x) { Graphics2D g2 = (Graphics2D) getGraphics(); Rectangle2D dataArea = getScreenDataArea(); g2.setXORMode(java.awt.Color.orange); if(((int) dataArea.getMinX() < x) && (x < (int) dataArea.getMaxX())) { if(this.verticalTraceLine != null) { g2.draw(this.verticalTraceLine); this.verticalTraceLine.setLine(x, (int) dataArea.getMinY(), x, (int) dataArea.getMaxY()); } else { this.verticalTraceLine = new Line2D.Float(x, (int) dataArea.getMinY(), x, (int) dataArea.getMaxY()); } g2.draw(this.verticalTraceLine); } } /** * Draws a horizontal line used to trace the mouse position to the vertical * axis. * * @param y * the y-coordinate of the trace line. */ private void drawVerticalAxisTrace(int y) { Graphics2D g2 = (Graphics2D) getGraphics(); Rectangle2D dataArea = getScreenDataArea(); g2.setXORMode(java.awt.Color.orange); if(((int) dataArea.getMinY() < y) && (y < (int) dataArea.getMaxY())) { if(this.horizontalTraceLine != null) { g2.draw(this.horizontalTraceLine); this.horizontalTraceLine.setLine((int) dataArea.getMinX(), y, (int) dataArea.getMaxX(), y); } else { this.horizontalTraceLine = new Line2D.Float((int) dataArea.getMinX(), y, (int) dataArea.getMaxX(), y); } g2.draw(this.horizontalTraceLine); } } /** * Displays a dialog that allows the user to edit the properties for the * current chart. */ private void attemptEditChartProperties() { ChartEditor editor = ChartEditorManager.getChartEditor(this.chart); int result = JOptionPane.showConfirmDialog(this, editor, localizationResources.getString("Chart_Properties"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if(result == JOptionPane.OK_OPTION) { editor.updateChart(this.chart); } } /** * Opens a file chooser and gives the user an opportunity to save the chart * in PNG format. * * @throws IOException * if there is an I/O error. */ public void doSaveAs() throws IOException { JFileChooser fileChooser = new JFileChooser(); ExtensionFileFilter filter = new ExtensionFileFilter(localizationResources.getString("PNG_Image_Files"), ".png"); fileChooser.addChoosableFileFilter(filter); int option = fileChooser.showSaveDialog(this); if(option == JFileChooser.APPROVE_OPTION) { String filename = fileChooser.getSelectedFile().getPath(); if(isEnforceFileExtensions()) { if(!filename.endsWith(".png")) { filename = filename + ".png"; } } ChartUtilities.saveChartAsPNG(new File(filename), this.chart, getWidth(), getHeight()); } } /** * Creates a print job for the chart. */ public void createChartPrintJob() { PrinterJob job = PrinterJob.getPrinterJob(); PageFormat pf = job.defaultPage(); PageFormat pf2 = job.pageDialog(pf); if(pf2 != pf) { job.setPrintable(this, pf2); if(job.printDialog()) { try { job.print(); } catch (PrinterException e) { JOptionPane.showMessageDialog(this, e); } } } } /** * Prints the chart on a single page. * * @param g * the graphics context. * @param pf * the page format to use. * @param pageIndex * the index of the page. If not 0, nothing gets * print. * @return The result of printing. */ public int print(Graphics g, PageFormat pf, int pageIndex) { if(pageIndex != 0) { return NO_SUCH_PAGE; } Graphics2D g2 = (Graphics2D) g; double x = pf.getImageableX(); double y = pf.getImageableY(); double w = pf.getImageableWidth(); double h = pf.getImageableHeight(); this.chart.draw(g2, new Rectangle2D.Double(x, y, w, h), this.anchor, null); return PAGE_EXISTS; } /** * Adds a listener to the list of objects listening for chart mouse events. * * @param listener * the listener (null not permitted). */ public void addChartMouseListener(ChartMouseListener listener) { if(listener == null) { throw new IllegalArgumentException("Null 'listener' argument."); } this.chartMouseListeners.add(ChartMouseListener.class, listener); } /** * Removes a listener from the list of objects listening for chart mouse * events. * * @param listener * the listener. */ public void removeChartMouseListener(ChartMouseListener listener) { this.chartMouseListeners.remove(ChartMouseListener.class, listener); } /** * Returns an array of the listeners of the given type registered with the * panel. * * @param listenerType * the listener type. * @return An array of listeners. */ public EventListener[] getListeners(Class listenerType) { if(listenerType == ChartMouseListener.class) { // fetch listeners from local storage return this.chartMouseListeners.getListeners(listenerType); } else { return super.getListeners(listenerType); } } /** * Creates a popup menu for the panel. * * @param properties * include a menu item for the chart property editor. * @param save * include a menu item for saving the chart. * @param print * include a menu item for printing the chart. * @param zoom * include menu items for zooming. * @return The popup menu. */ protected JPopupMenu createPopupMenu(boolean properties, boolean save, boolean print, boolean zoom) { JPopupMenu result = new JPopupMenu("Chart:"); boolean separator = false; if(properties) { JMenuItem propertiesItem = new JMenuItem(localizationResources.getString("Properties...")); propertiesItem.setActionCommand(PROPERTIES_COMMAND); propertiesItem.addActionListener(this); result.add(propertiesItem); separator = true; } if(save) { if(separator) { result.addSeparator(); separator = false; } JMenuItem saveItem = new JMenuItem(localizationResources.getString("Save_as...")); saveItem.setActionCommand(SAVE_COMMAND); saveItem.addActionListener(this); result.add(saveItem); separator = true; } if(print) { if(separator) { result.addSeparator(); separator = false; } JMenuItem printItem = new JMenuItem(localizationResources.getString("Print...")); printItem.setActionCommand(PRINT_COMMAND); printItem.addActionListener(this); result.add(printItem); separator = true; } if(zoom) { if(separator) { result.addSeparator(); separator = false; } JMenu zoomInMenu = new JMenu(localizationResources.getString("Zoom_In")); this.zoomInBothMenuItem = new JMenuItem(localizationResources.getString("All_Axes")); this.zoomInBothMenuItem.setActionCommand(ZOOM_IN_BOTH_COMMAND); this.zoomInBothMenuItem.addActionListener(this); zoomInMenu.add(this.zoomInBothMenuItem); zoomInMenu.addSeparator(); this.zoomInDomainMenuItem = new JMenuItem(localizationResources.getString("Domain_Axis")); this.zoomInDomainMenuItem.setActionCommand(ZOOM_IN_DOMAIN_COMMAND); this.zoomInDomainMenuItem.addActionListener(this); zoomInMenu.add(this.zoomInDomainMenuItem); this.zoomInRangeMenuItem = new JMenuItem(localizationResources.getString("Range_Axis")); this.zoomInRangeMenuItem.setActionCommand(ZOOM_IN_RANGE_COMMAND); this.zoomInRangeMenuItem.addActionListener(this); zoomInMenu.add(this.zoomInRangeMenuItem); result.add(zoomInMenu); JMenu zoomOutMenu = new JMenu(localizationResources.getString("Zoom_Out")); this.zoomOutBothMenuItem = new JMenuItem(localizationResources.getString("All_Axes")); this.zoomOutBothMenuItem.setActionCommand(ZOOM_OUT_BOTH_COMMAND); this.zoomOutBothMenuItem.addActionListener(this); zoomOutMenu.add(this.zoomOutBothMenuItem); zoomOutMenu.addSeparator(); this.zoomOutDomainMenuItem = new JMenuItem(localizationResources.getString("Domain_Axis")); this.zoomOutDomainMenuItem.setActionCommand(ZOOM_OUT_DOMAIN_COMMAND); this.zoomOutDomainMenuItem.addActionListener(this); zoomOutMenu.add(this.zoomOutDomainMenuItem); this.zoomOutRangeMenuItem = new JMenuItem(localizationResources.getString("Range_Axis")); this.zoomOutRangeMenuItem.setActionCommand(ZOOM_OUT_RANGE_COMMAND); this.zoomOutRangeMenuItem.addActionListener(this); zoomOutMenu.add(this.zoomOutRangeMenuItem); result.add(zoomOutMenu); JMenu autoRangeMenu = new JMenu(localizationResources.getString("Auto_Range")); this.zoomResetBothMenuItem = new JMenuItem(localizationResources.getString("All_Axes")); this.zoomResetBothMenuItem.setActionCommand(ZOOM_RESET_BOTH_COMMAND); this.zoomResetBothMenuItem.addActionListener(this); autoRangeMenu.add(this.zoomResetBothMenuItem); autoRangeMenu.addSeparator(); this.zoomResetDomainMenuItem = new JMenuItem(localizationResources.getString("Domain_Axis")); this.zoomResetDomainMenuItem.setActionCommand(ZOOM_RESET_DOMAIN_COMMAND); this.zoomResetDomainMenuItem.addActionListener(this); autoRangeMenu.add(this.zoomResetDomainMenuItem); this.zoomResetRangeMenuItem = new JMenuItem(localizationResources.getString("Range_Axis")); this.zoomResetRangeMenuItem.setActionCommand(ZOOM_RESET_RANGE_COMMAND); this.zoomResetRangeMenuItem.addActionListener(this); autoRangeMenu.add(this.zoomResetRangeMenuItem); result.addSeparator(); result.add(autoRangeMenu); } return result; } /** * The idea is to modify the zooming options depending on the type of chart * being displayed by the panel. * * @param x * horizontal position of the popup. * @param y * vertical position of the popup. */ protected void displayPopupMenu(int x, int y) { if(this.popup != null) { // go through each zoom menu item and decide whether or not to // enable it... Plot plot = this.chart.getPlot(); boolean isDomainZoomable = false; boolean isRangeZoomable = false; if(plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; isDomainZoomable = z.isDomainZoomable(); isRangeZoomable = z.isRangeZoomable(); } if(this.zoomInDomainMenuItem != null) { this.zoomInDomainMenuItem.setEnabled(isDomainZoomable); } if(this.zoomOutDomainMenuItem != null) { this.zoomOutDomainMenuItem.setEnabled(isDomainZoomable); } if(this.zoomResetDomainMenuItem != null) { this.zoomResetDomainMenuItem.setEnabled(isDomainZoomable); } if(this.zoomInRangeMenuItem != null) { this.zoomInRangeMenuItem.setEnabled(isRangeZoomable); } if(this.zoomOutRangeMenuItem != null) { this.zoomOutRangeMenuItem.setEnabled(isRangeZoomable); } if(this.zoomResetRangeMenuItem != null) { this.zoomResetRangeMenuItem.setEnabled(isRangeZoomable); } if(this.zoomInBothMenuItem != null) { this.zoomInBothMenuItem.setEnabled(isDomainZoomable & isRangeZoomable); } if(this.zoomOutBothMenuItem != null) { this.zoomOutBothMenuItem.setEnabled(isDomainZoomable & isRangeZoomable); } if(this.zoomResetBothMenuItem != null) { this.zoomResetBothMenuItem.setEnabled(isDomainZoomable & isRangeZoomable); } this.popup.show(this, x, y); } } public Rectangle2D getZoomRectangle() { return zoomRectangle; } public PlotOrientation getOrientation() { return orientation; } public void setOrientation(PlotOrientation orientation) { this.orientation = orientation; } public Point getZoomPoint() { return zoomPoint; } public void setZoomPoint(Point zoomPoint) { this.zoomPoint = zoomPoint; } public void setZoomRectangle(Rectangle2D zoomRectangle) { this.zoomRectangle = zoomRectangle; } }

Ads help maintain this website.