0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ---------------
0028: * ChartPanel.java
0029: * ---------------
0030: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Andrzej Porebski;
0034: * Soren Caspersen;
0035: * Jonathan Nash;
0036: * Hans-Jurgen Greiner;
0037: * Andreas Schneider;
0038: * Daniel van Enckevort;
0039: * David M O'Donnell;
0040: * Arnaud Lelievre;
0041: * Matthias Rose;
0042: * Onno vd Akker;
0043: * Sergei Ivanov;
0044: *
0045: * $Id: ChartPanel.java,v 1.20.2.15 2007/06/06 15:08:23 mungady Exp $
0046: *
0047: * Changes (from 28-Jun-2001)
0048: * --------------------------
0049: * 28-Jun-2001 : Integrated buffering code contributed by S???ren
0050: * Caspersen (DG);
0051: * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
0052: * 22-Nov-2001 : Added scaling to improve display of charts in small sizes (DG);
0053: * 26-Nov-2001 : Added property editing, saving and printing (DG);
0054: * 11-Dec-2001 : Transferred saveChartAsPNG method to new ChartUtilities
0055: * class (DG);
0056: * 13-Dec-2001 : Added tooltips (DG);
0057: * 16-Jan-2002 : Added an optional crosshair, based on the implementation by
0058: * Jonathan Nash. Renamed the tooltips class (DG);
0059: * 23-Jan-2002 : Implemented zooming based on code by Hans-Jurgen Greiner (DG);
0060: * 05-Feb-2002 : Improved tooltips setup. Renamed method attemptSaveAs()
0061: * --> doSaveAs() and made it public rather than private (DG);
0062: * 28-Mar-2002 : Added a new constructor (DG);
0063: * 09-Apr-2002 : Changed initialisation of tooltip generation, as suggested by
0064: * Hans-Jurgen Greiner (DG);
0065: * 27-May-2002 : New interactive zooming methods based on code by Hans-Jurgen
0066: * Greiner. Renamed JFreeChartPanel --> ChartPanel, moved
0067: * constants to ChartPanelConstants interface (DG);
0068: * 31-May-2002 : Fixed a bug with interactive zooming and added a way to
0069: * control if the zoom rectangle is filled in or drawn as an
0070: * outline. A mouse drag gesture towards the top left now causes
0071: * an autoRangeBoth() and is a way to undo zooms (AS);
0072: * 11-Jun-2002 : Reinstated handleClick method call in mouseClicked() to get
0073: * crosshairs working again (DG);
0074: * 13-Jun-2002 : Added check for null popup menu in mouseDragged method (DG);
0075: * 18-Jun-2002 : Added get/set methods for minimum and maximum chart
0076: * dimensions (DG);
0077: * 25-Jun-2002 : Removed redundant code (DG);
0078: * 27-Aug-2002 : Added get/set methods for popup menu (DG);
0079: * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
0080: * 22-Oct-2002 : Added translation methods for screen <--> Java2D, contributed
0081: * by Daniel van Enckevort (DG);
0082: * 05-Nov-2002 : Added a chart reference to the ChartMouseEvent class (DG);
0083: * 22-Nov-2002 : Added test in zoom method for inverted axes, supplied by
0084: * David M O'Donnell (DG);
0085: * 14-Jan-2003 : Implemented ChartProgressListener interface (DG);
0086: * 14-Feb-2003 : Removed deprecated setGenerateTooltips method (DG);
0087: * 12-Mar-2003 : Added option to enforce filename extension (see bug id
0088: * 643173) (DG);
0089: * 08-Sep-2003 : Added internationalization via use of properties
0090: * resourceBundle (RFE 690236) (AL);
0091: * 18-Sep-2003 : Added getScaleX() and getScaleY() methods (protected) as
0092: * requested by Irv Thomae (DG);
0093: * 12-Nov-2003 : Added zooming support for the FastScatterPlot class (DG);
0094: * 24-Nov-2003 : Minor Javadoc updates (DG);
0095: * 04-Dec-2003 : Added anchor point for crosshair calculation (DG);
0096: * 17-Jan-2004 : Added new methods to set tooltip delays to be used in this
0097: * chart panel. Refer to patch 877565 (MR);
0098: * 02-Feb-2004 : Fixed bug in zooming trigger and added zoomTriggerDistance
0099: * attribute (DG);
0100: * 08-Apr-2004 : Changed getScaleX() and getScaleY() from protected to
0101: * public (DG);
0102: * 15-Apr-2004 : Added zoomOutFactor and zoomInFactor (DG);
0103: * 21-Apr-2004 : Fixed zooming bug in mouseReleased() method (DG);
0104: * 13-Jul-2004 : Added check for null chart (DG);
0105: * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
0106: * 11-Nov-2004 : Moved constants back in from ChartPanelConstants (DG);
0107: * 12-Nov-2004 : Modified zooming mechanism to support zooming within
0108: * subplots (DG);
0109: * 26-Jan-2005 : Fixed mouse zooming for horizontal category plots (DG);
0110: * 11-Apr-2005 : Added getFillZoomRectangle() method, renamed
0111: * setHorizontalZoom() --> setDomainZoomable(),
0112: * setVerticalZoom() --> setRangeZoomable(), added
0113: * isDomainZoomable() and isRangeZoomable(), added
0114: * getHorizontalAxisTrace() and getVerticalAxisTrace(),
0115: * renamed autoRangeBoth() --> restoreAutoBounds(),
0116: * autoRangeHorizontal() --> restoreAutoDomainBounds(),
0117: * autoRangeVertical() --> restoreAutoRangeBounds() (DG);
0118: * 12-Apr-2005 : Removed working areas, added getAnchorPoint() method,
0119: * added protected accessors for tracelines (DG);
0120: * 18-Apr-2005 : Made constants final (DG);
0121: * 26-Apr-2005 : Removed LOGGER (DG);
0122: * 01-Jun-2005 : Fixed zooming for combined plots - see bug report
0123: * 1212039, fix thanks to Onno vd Akker (DG);
0124: * 25-Nov-2005 : Reworked event listener mechanism (DG);
0125: * ------------- JFREECHART 1.0.x ---------------------------------------------
0126: * 01-Aug-2006 : Fixed minor bug in restoreAutoRangeBounds() (DG);
0127: * 04-Sep-2006 : Renamed attemptEditChartProperties() -->
0128: * doEditChartProperties() and made public (DG);
0129: * 13-Sep-2006 : Don't generate ChartMouseEvents if the panel's chart is null
0130: * (fixes bug 1556951) (DG);
0131: * 05-Mar-2007 : Applied patch 1672561 by Sergei Ivanov, to fix zoom rectangle
0132: * drawing for dynamic charts (DG);
0133: * 17-Apr-2007 : Fix NullPointerExceptions in zooming for combined plots (DG);
0134: * 24-May-2007 : When the look-and-feel changes, update the popup menu if there
0135: * is one (DG);
0136: * 06-Jun-2007 : Fixed coordinates for drawing buffer image (DG);
0137: *
0138: */
0139:
0140: package org.jfree.chart;
0141:
0142: import java.awt.AWTEvent;
0143: import java.awt.Color;
0144: import java.awt.Dimension;
0145: import java.awt.Graphics;
0146: import java.awt.Graphics2D;
0147: import java.awt.Image;
0148: import java.awt.Insets;
0149: import java.awt.Point;
0150: import java.awt.event.ActionEvent;
0151: import java.awt.event.ActionListener;
0152: import java.awt.event.MouseEvent;
0153: import java.awt.event.MouseListener;
0154: import java.awt.event.MouseMotionListener;
0155: import java.awt.geom.AffineTransform;
0156: import java.awt.geom.Line2D;
0157: import java.awt.geom.Point2D;
0158: import java.awt.geom.Rectangle2D;
0159: import java.awt.print.PageFormat;
0160: import java.awt.print.Printable;
0161: import java.awt.print.PrinterException;
0162: import java.awt.print.PrinterJob;
0163: import java.io.File;
0164: import java.io.IOException;
0165: import java.io.Serializable;
0166: import java.util.EventListener;
0167: import java.util.ResourceBundle;
0168:
0169: import javax.swing.JFileChooser;
0170: import javax.swing.JMenu;
0171: import javax.swing.JMenuItem;
0172: import javax.swing.JOptionPane;
0173: import javax.swing.JPanel;
0174: import javax.swing.JPopupMenu;
0175: import javax.swing.SwingUtilities;
0176: import javax.swing.ToolTipManager;
0177: import javax.swing.event.EventListenerList;
0178:
0179: import org.jfree.chart.editor.ChartEditor;
0180: import org.jfree.chart.editor.ChartEditorManager;
0181: import org.jfree.chart.entity.ChartEntity;
0182: import org.jfree.chart.entity.EntityCollection;
0183: import org.jfree.chart.event.ChartChangeEvent;
0184: import org.jfree.chart.event.ChartChangeListener;
0185: import org.jfree.chart.event.ChartProgressEvent;
0186: import org.jfree.chart.event.ChartProgressListener;
0187: import org.jfree.chart.plot.Plot;
0188: import org.jfree.chart.plot.PlotOrientation;
0189: import org.jfree.chart.plot.PlotRenderingInfo;
0190: import org.jfree.chart.plot.Zoomable;
0191: import org.jfree.ui.ExtensionFileFilter;
0192:
0193: /**
0194: * A Swing GUI component for displaying a {@link JFreeChart} object.
0195: * <P>
0196: * The panel registers with the chart to receive notification of changes to any
0197: * component of the chart. The chart is redrawn automatically whenever this
0198: * notification is received.
0199: */
0200: public class ChartPanel extends JPanel implements ChartChangeListener,
0201: ChartProgressListener, ActionListener, MouseListener,
0202: MouseMotionListener, Printable, Serializable {
0203:
0204: /** For serialization. */
0205: private static final long serialVersionUID = 6046366297214274674L;
0206:
0207: /** Default setting for buffer usage. */
0208: public static final boolean DEFAULT_BUFFER_USED = false;
0209:
0210: /** The default panel width. */
0211: public static final int DEFAULT_WIDTH = 680;
0212:
0213: /** The default panel height. */
0214: public static final int DEFAULT_HEIGHT = 420;
0215:
0216: /** The default limit below which chart scaling kicks in. */
0217: public static final int DEFAULT_MINIMUM_DRAW_WIDTH = 300;
0218:
0219: /** The default limit below which chart scaling kicks in. */
0220: public static final int DEFAULT_MINIMUM_DRAW_HEIGHT = 200;
0221:
0222: /** The default limit below which chart scaling kicks in. */
0223: public static final int DEFAULT_MAXIMUM_DRAW_WIDTH = 800;
0224:
0225: /** The default limit below which chart scaling kicks in. */
0226: public static final int DEFAULT_MAXIMUM_DRAW_HEIGHT = 600;
0227:
0228: /** The minimum size required to perform a zoom on a rectangle */
0229: public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10;
0230:
0231: /** Properties action command. */
0232: public static final String PROPERTIES_COMMAND = "PROPERTIES";
0233:
0234: /** Save action command. */
0235: public static final String SAVE_COMMAND = "SAVE";
0236:
0237: /** Print action command. */
0238: public static final String PRINT_COMMAND = "PRINT";
0239:
0240: /** Zoom in (both axes) action command. */
0241: public static final String ZOOM_IN_BOTH_COMMAND = "ZOOM_IN_BOTH";
0242:
0243: /** Zoom in (domain axis only) action command. */
0244: public static final String ZOOM_IN_DOMAIN_COMMAND = "ZOOM_IN_DOMAIN";
0245:
0246: /** Zoom in (range axis only) action command. */
0247: public static final String ZOOM_IN_RANGE_COMMAND = "ZOOM_IN_RANGE";
0248:
0249: /** Zoom out (both axes) action command. */
0250: public static final String ZOOM_OUT_BOTH_COMMAND = "ZOOM_OUT_BOTH";
0251:
0252: /** Zoom out (domain axis only) action command. */
0253: public static final String ZOOM_OUT_DOMAIN_COMMAND = "ZOOM_DOMAIN_BOTH";
0254:
0255: /** Zoom out (range axis only) action command. */
0256: public static final String ZOOM_OUT_RANGE_COMMAND = "ZOOM_RANGE_BOTH";
0257:
0258: /** Zoom reset (both axes) action command. */
0259: public static final String ZOOM_RESET_BOTH_COMMAND = "ZOOM_RESET_BOTH";
0260:
0261: /** Zoom reset (domain axis only) action command. */
0262: public static final String ZOOM_RESET_DOMAIN_COMMAND = "ZOOM_RESET_DOMAIN";
0263:
0264: /** Zoom reset (range axis only) action command. */
0265: public static final String ZOOM_RESET_RANGE_COMMAND = "ZOOM_RESET_RANGE";
0266:
0267: /** The chart that is displayed in the panel. */
0268: private JFreeChart chart;
0269:
0270: /** Storage for registered (chart) mouse listeners. */
0271: private EventListenerList chartMouseListeners;
0272:
0273: /** A flag that controls whether or not the off-screen buffer is used. */
0274: private boolean useBuffer;
0275:
0276: /** A flag that indicates that the buffer should be refreshed. */
0277: private boolean refreshBuffer;
0278:
0279: /** A buffer for the rendered chart. */
0280: private Image chartBuffer;
0281:
0282: /** The height of the chart buffer. */
0283: private int chartBufferHeight;
0284:
0285: /** The width of the chart buffer. */
0286: private int chartBufferWidth;
0287:
0288: /**
0289: * The minimum width for drawing a chart (uses scaling for smaller widths).
0290: */
0291: private int minimumDrawWidth;
0292:
0293: /**
0294: * The minimum height for drawing a chart (uses scaling for smaller
0295: * heights).
0296: */
0297: private int minimumDrawHeight;
0298:
0299: /**
0300: * The maximum width for drawing a chart (uses scaling for bigger
0301: * widths).
0302: */
0303: private int maximumDrawWidth;
0304:
0305: /**
0306: * The maximum height for drawing a chart (uses scaling for bigger
0307: * heights).
0308: */
0309: private int maximumDrawHeight;
0310:
0311: /** The popup menu for the frame. */
0312: private JPopupMenu popup;
0313:
0314: /** The drawing info collected the last time the chart was drawn. */
0315: private ChartRenderingInfo info;
0316:
0317: /** The chart anchor point. */
0318: private Point2D anchor;
0319:
0320: /** The scale factor used to draw the chart. */
0321: private double scaleX;
0322:
0323: /** The scale factor used to draw the chart. */
0324: private double scaleY;
0325:
0326: /** The plot orientation. */
0327: private PlotOrientation orientation = PlotOrientation.VERTICAL;
0328:
0329: /** A flag that controls whether or not domain zooming is enabled. */
0330: private boolean domainZoomable = false;
0331:
0332: /** A flag that controls whether or not range zooming is enabled. */
0333: private boolean rangeZoomable = false;
0334:
0335: /**
0336: * The zoom rectangle starting point (selected by the user with a mouse
0337: * click). This is a point on the screen, not the chart (which may have
0338: * been scaled up or down to fit the panel).
0339: */
0340: private Point zoomPoint = null;
0341:
0342: /** The zoom rectangle (selected by the user with the mouse). */
0343: private transient Rectangle2D zoomRectangle = null;
0344:
0345: /** Controls if the zoom rectangle is drawn as an outline or filled. */
0346: private boolean fillZoomRectangle = false;
0347:
0348: /** The minimum distance required to drag the mouse to trigger a zoom. */
0349: private int zoomTriggerDistance;
0350:
0351: /** A flag that controls whether or not horizontal tracing is enabled. */
0352: private boolean horizontalAxisTrace = false;
0353:
0354: /** A flag that controls whether or not vertical tracing is enabled. */
0355: private boolean verticalAxisTrace = false;
0356:
0357: /** A vertical trace line. */
0358: private transient Line2D verticalTraceLine;
0359:
0360: /** A horizontal trace line. */
0361: private transient Line2D horizontalTraceLine;
0362:
0363: /** Menu item for zooming in on a chart (both axes). */
0364: private JMenuItem zoomInBothMenuItem;
0365:
0366: /** Menu item for zooming in on a chart (domain axis). */
0367: private JMenuItem zoomInDomainMenuItem;
0368:
0369: /** Menu item for zooming in on a chart (range axis). */
0370: private JMenuItem zoomInRangeMenuItem;
0371:
0372: /** Menu item for zooming out on a chart. */
0373: private JMenuItem zoomOutBothMenuItem;
0374:
0375: /** Menu item for zooming out on a chart (domain axis). */
0376: private JMenuItem zoomOutDomainMenuItem;
0377:
0378: /** Menu item for zooming out on a chart (range axis). */
0379: private JMenuItem zoomOutRangeMenuItem;
0380:
0381: /** Menu item for resetting the zoom (both axes). */
0382: private JMenuItem zoomResetBothMenuItem;
0383:
0384: /** Menu item for resetting the zoom (domain axis only). */
0385: private JMenuItem zoomResetDomainMenuItem;
0386:
0387: /** Menu item for resetting the zoom (range axis only). */
0388: private JMenuItem zoomResetRangeMenuItem;
0389:
0390: /** A flag that controls whether or not file extensions are enforced. */
0391: private boolean enforceFileExtensions;
0392:
0393: /** A flag that indicates if original tooltip delays are changed. */
0394: private boolean ownToolTipDelaysActive;
0395:
0396: /** Original initial tooltip delay of ToolTipManager.sharedInstance(). */
0397: private int originalToolTipInitialDelay;
0398:
0399: /** Original reshow tooltip delay of ToolTipManager.sharedInstance(). */
0400: private int originalToolTipReshowDelay;
0401:
0402: /** Original dismiss tooltip delay of ToolTipManager.sharedInstance(). */
0403: private int originalToolTipDismissDelay;
0404:
0405: /** Own initial tooltip delay to be used in this chart panel. */
0406: private int ownToolTipInitialDelay;
0407:
0408: /** Own reshow tooltip delay to be used in this chart panel. */
0409: private int ownToolTipReshowDelay;
0410:
0411: /** Own dismiss tooltip delay to be used in this chart panel. */
0412: private int ownToolTipDismissDelay;
0413:
0414: /** The factor used to zoom in on an axis range. */
0415: private double zoomInFactor = 0.5;
0416:
0417: /** The factor used to zoom out on an axis range. */
0418: private double zoomOutFactor = 2.0;
0419:
0420: /** The resourceBundle for the localization. */
0421: protected static ResourceBundle localizationResources = ResourceBundle
0422: .getBundle("org.jfree.chart.LocalizationBundle");
0423:
0424: /**
0425: * Constructs a panel that displays the specified chart.
0426: *
0427: * @param chart the chart.
0428: */
0429: public ChartPanel(JFreeChart chart) {
0430:
0431: this (chart, DEFAULT_WIDTH, DEFAULT_HEIGHT,
0432: DEFAULT_MINIMUM_DRAW_WIDTH,
0433: DEFAULT_MINIMUM_DRAW_HEIGHT,
0434: DEFAULT_MAXIMUM_DRAW_WIDTH,
0435: DEFAULT_MAXIMUM_DRAW_HEIGHT, DEFAULT_BUFFER_USED, true, // properties
0436: true, // save
0437: true, // print
0438: true, // zoom
0439: true // tooltips
0440: );
0441:
0442: }
0443:
0444: /**
0445: * Constructs a panel containing a chart.
0446: *
0447: * @param chart the chart.
0448: * @param useBuffer a flag controlling whether or not an off-screen buffer
0449: * is used.
0450: */
0451: public ChartPanel(JFreeChart chart, boolean useBuffer) {
0452:
0453: this (chart, DEFAULT_WIDTH, DEFAULT_HEIGHT,
0454: DEFAULT_MINIMUM_DRAW_WIDTH,
0455: DEFAULT_MINIMUM_DRAW_HEIGHT,
0456: DEFAULT_MAXIMUM_DRAW_WIDTH,
0457: DEFAULT_MAXIMUM_DRAW_HEIGHT, useBuffer, true, // properties
0458: true, // save
0459: true, // print
0460: true, // zoom
0461: true // tooltips
0462: );
0463:
0464: }
0465:
0466: /**
0467: * Constructs a JFreeChart panel.
0468: *
0469: * @param chart the chart.
0470: * @param properties a flag indicating whether or not the chart property
0471: * editor should be available via the popup menu.
0472: * @param save a flag indicating whether or not save options should be
0473: * available via the popup menu.
0474: * @param print a flag indicating whether or not the print option
0475: * should be available via the popup menu.
0476: * @param zoom a flag indicating whether or not zoom options should
0477: * be added to the popup menu.
0478: * @param tooltips a flag indicating whether or not tooltips should be
0479: * enabled for the chart.
0480: */
0481: public ChartPanel(JFreeChart chart, boolean properties,
0482: boolean save, boolean print, boolean zoom, boolean tooltips) {
0483:
0484: this (chart, DEFAULT_WIDTH, DEFAULT_HEIGHT,
0485: DEFAULT_MINIMUM_DRAW_WIDTH,
0486: DEFAULT_MINIMUM_DRAW_HEIGHT,
0487: DEFAULT_MAXIMUM_DRAW_WIDTH,
0488: DEFAULT_MAXIMUM_DRAW_HEIGHT, DEFAULT_BUFFER_USED,
0489: properties, save, print, zoom, tooltips);
0490:
0491: }
0492:
0493: /**
0494: * Constructs a JFreeChart panel.
0495: *
0496: * @param chart the chart.
0497: * @param width the preferred width of the panel.
0498: * @param height the preferred height of the panel.
0499: * @param minimumDrawWidth the minimum drawing width.
0500: * @param minimumDrawHeight the minimum drawing height.
0501: * @param maximumDrawWidth the maximum drawing width.
0502: * @param maximumDrawHeight the maximum drawing height.
0503: * @param useBuffer a flag that indicates whether to use the off-screen
0504: * buffer to improve performance (at the expense of
0505: * memory).
0506: * @param properties a flag indicating whether or not the chart property
0507: * editor should be available via the popup menu.
0508: * @param save a flag indicating whether or not save options should be
0509: * available via the popup menu.
0510: * @param print a flag indicating whether or not the print option
0511: * should be available via the popup menu.
0512: * @param zoom a flag indicating whether or not zoom options should be
0513: * added to the popup menu.
0514: * @param tooltips a flag indicating whether or not tooltips should be
0515: * enabled for the chart.
0516: */
0517: public ChartPanel(JFreeChart chart, int width, int height,
0518: int minimumDrawWidth, int minimumDrawHeight,
0519: int maximumDrawWidth, int maximumDrawHeight,
0520: boolean useBuffer, boolean properties, boolean save,
0521: boolean print, boolean zoom, boolean tooltips) {
0522:
0523: this .setChart(chart);
0524: this .chartMouseListeners = new EventListenerList();
0525: this .info = new ChartRenderingInfo();
0526: setPreferredSize(new Dimension(width, height));
0527: this .useBuffer = useBuffer;
0528: this .refreshBuffer = false;
0529: this .minimumDrawWidth = minimumDrawWidth;
0530: this .minimumDrawHeight = minimumDrawHeight;
0531: this .maximumDrawWidth = maximumDrawWidth;
0532: this .maximumDrawHeight = maximumDrawHeight;
0533: this .zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE;
0534:
0535: // set up popup menu...
0536: this .popup = null;
0537: if (properties || save || print || zoom) {
0538: this .popup = createPopupMenu(properties, save, print, zoom);
0539: }
0540:
0541: enableEvents(AWTEvent.MOUSE_EVENT_MASK);
0542: enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
0543: setDisplayToolTips(tooltips);
0544: addMouseListener(this );
0545: addMouseMotionListener(this );
0546:
0547: this .enforceFileExtensions = true;
0548:
0549: // initialize ChartPanel-specific tool tip delays with
0550: // values the from ToolTipManager.sharedInstance()
0551: ToolTipManager ttm = ToolTipManager.sharedInstance();
0552: this .ownToolTipInitialDelay = ttm.getInitialDelay();
0553: this .ownToolTipDismissDelay = ttm.getDismissDelay();
0554: this .ownToolTipReshowDelay = ttm.getReshowDelay();
0555:
0556: }
0557:
0558: /**
0559: * Returns the chart contained in the panel.
0560: *
0561: * @return The chart (possibly <code>null</code>).
0562: */
0563: public JFreeChart getChart() {
0564: return this .chart;
0565: }
0566:
0567: /**
0568: * Sets the chart that is displayed in the panel.
0569: *
0570: * @param chart the chart (<code>null</code> permitted).
0571: */
0572: public void setChart(JFreeChart chart) {
0573:
0574: // stop listening for changes to the existing chart
0575: if (this .chart != null) {
0576: this .chart.removeChangeListener(this );
0577: this .chart.removeProgressListener(this );
0578: }
0579:
0580: // add the new chart
0581: this .chart = chart;
0582: if (chart != null) {
0583: this .chart.addChangeListener(this );
0584: this .chart.addProgressListener(this );
0585: Plot plot = chart.getPlot();
0586: this .domainZoomable = false;
0587: this .rangeZoomable = false;
0588: if (plot instanceof Zoomable) {
0589: Zoomable z = (Zoomable) plot;
0590: this .domainZoomable = z.isDomainZoomable();
0591: this .rangeZoomable = z.isRangeZoomable();
0592: this .orientation = z.getOrientation();
0593: }
0594: } else {
0595: this .domainZoomable = false;
0596: this .rangeZoomable = false;
0597: }
0598: if (this .useBuffer) {
0599: this .refreshBuffer = true;
0600: }
0601: repaint();
0602:
0603: }
0604:
0605: /**
0606: * Returns the minimum drawing width for charts.
0607: * <P>
0608: * If the width available on the panel is less than this, then the chart is
0609: * drawn at the minimum width then scaled down to fit.
0610: *
0611: * @return The minimum drawing width.
0612: */
0613: public int getMinimumDrawWidth() {
0614: return this .minimumDrawWidth;
0615: }
0616:
0617: /**
0618: * Sets the minimum drawing width for the chart on this panel.
0619: * <P>
0620: * At the time the chart is drawn on the panel, if the available width is
0621: * less than this amount, the chart will be drawn using the minimum width
0622: * then scaled down to fit the available space.
0623: *
0624: * @param width The width.
0625: */
0626: public void setMinimumDrawWidth(int width) {
0627: this .minimumDrawWidth = width;
0628: }
0629:
0630: /**
0631: * Returns the maximum drawing width for charts.
0632: * <P>
0633: * If the width available on the panel is greater than this, then the chart
0634: * is drawn at the maximum width then scaled up to fit.
0635: *
0636: * @return The maximum drawing width.
0637: */
0638: public int getMaximumDrawWidth() {
0639: return this .maximumDrawWidth;
0640: }
0641:
0642: /**
0643: * Sets the maximum drawing width for the chart on this panel.
0644: * <P>
0645: * At the time the chart is drawn on the panel, if the available width is
0646: * greater than this amount, the chart will be drawn using the maximum
0647: * width then scaled up to fit the available space.
0648: *
0649: * @param width The width.
0650: */
0651: public void setMaximumDrawWidth(int width) {
0652: this .maximumDrawWidth = width;
0653: }
0654:
0655: /**
0656: * Returns the minimum drawing height for charts.
0657: * <P>
0658: * If the height available on the panel is less than this, then the chart
0659: * is drawn at the minimum height then scaled down to fit.
0660: *
0661: * @return The minimum drawing height.
0662: */
0663: public int getMinimumDrawHeight() {
0664: return this .minimumDrawHeight;
0665: }
0666:
0667: /**
0668: * Sets the minimum drawing height for the chart on this panel.
0669: * <P>
0670: * At the time the chart is drawn on the panel, if the available height is
0671: * less than this amount, the chart will be drawn using the minimum height
0672: * then scaled down to fit the available space.
0673: *
0674: * @param height The height.
0675: */
0676: public void setMinimumDrawHeight(int height) {
0677: this .minimumDrawHeight = height;
0678: }
0679:
0680: /**
0681: * Returns the maximum drawing height for charts.
0682: * <P>
0683: * If the height available on the panel is greater than this, then the
0684: * chart is drawn at the maximum height then scaled up to fit.
0685: *
0686: * @return The maximum drawing height.
0687: */
0688: public int getMaximumDrawHeight() {
0689: return this .maximumDrawHeight;
0690: }
0691:
0692: /**
0693: * Sets the maximum drawing height for the chart on this panel.
0694: * <P>
0695: * At the time the chart is drawn on the panel, if the available height is
0696: * greater than this amount, the chart will be drawn using the maximum
0697: * height then scaled up to fit the available space.
0698: *
0699: * @param height The height.
0700: */
0701: public void setMaximumDrawHeight(int height) {
0702: this .maximumDrawHeight = height;
0703: }
0704:
0705: /**
0706: * Returns the X scale factor for the chart. This will be 1.0 if no
0707: * scaling has been used.
0708: *
0709: * @return The scale factor.
0710: */
0711: public double getScaleX() {
0712: return this .scaleX;
0713: }
0714:
0715: /**
0716: * Returns the Y scale factory for the chart. This will be 1.0 if no
0717: * scaling has been used.
0718: *
0719: * @return The scale factor.
0720: */
0721: public double getScaleY() {
0722: return this .scaleY;
0723: }
0724:
0725: /**
0726: * Returns the anchor point.
0727: *
0728: * @return The anchor point (possibly <code>null</code>).
0729: */
0730: public Point2D getAnchor() {
0731: return this .anchor;
0732: }
0733:
0734: /**
0735: * Sets the anchor point. This method is provided for the use of
0736: * subclasses, not end users.
0737: *
0738: * @param anchor the anchor point (<code>null</code> permitted).
0739: */
0740: protected void setAnchor(Point2D anchor) {
0741: this .anchor = anchor;
0742: }
0743:
0744: /**
0745: * Returns the popup menu.
0746: *
0747: * @return The popup menu.
0748: */
0749: public JPopupMenu getPopupMenu() {
0750: return this .popup;
0751: }
0752:
0753: /**
0754: * Sets the popup menu for the panel.
0755: *
0756: * @param popup the popup menu (<code>null</code> permitted).
0757: */
0758: public void setPopupMenu(JPopupMenu popup) {
0759: this .popup = popup;
0760: }
0761:
0762: /**
0763: * Returns the chart rendering info from the most recent chart redraw.
0764: *
0765: * @return The chart rendering info.
0766: */
0767: public ChartRenderingInfo getChartRenderingInfo() {
0768: return this .info;
0769: }
0770:
0771: /**
0772: * A convenience method that switches on mouse-based zooming.
0773: *
0774: * @param flag <code>true</code> enables zooming and rectangle fill on
0775: * zoom.
0776: */
0777: public void setMouseZoomable(boolean flag) {
0778: setMouseZoomable(flag, true);
0779: }
0780:
0781: /**
0782: * A convenience method that switches on mouse-based zooming.
0783: *
0784: * @param flag <code>true</code> if zooming enabled
0785: * @param fillRectangle <code>true</code> if zoom rectangle is filled,
0786: * false if rectangle is shown as outline only.
0787: */
0788: public void setMouseZoomable(boolean flag, boolean fillRectangle) {
0789: setDomainZoomable(flag);
0790: setRangeZoomable(flag);
0791: setFillZoomRectangle(fillRectangle);
0792: }
0793:
0794: /**
0795: * Returns the flag that determines whether or not zooming is enabled for
0796: * the domain axis.
0797: *
0798: * @return A boolean.
0799: */
0800: public boolean isDomainZoomable() {
0801: return this .domainZoomable;
0802: }
0803:
0804: /**
0805: * Sets the flag that controls whether or not zooming is enable for the
0806: * domain axis. A check is made to ensure that the current plot supports
0807: * zooming for the domain values.
0808: *
0809: * @param flag <code>true</code> enables zooming if possible.
0810: */
0811: public void setDomainZoomable(boolean flag) {
0812: if (flag) {
0813: Plot plot = this .chart.getPlot();
0814: if (plot instanceof Zoomable) {
0815: Zoomable z = (Zoomable) plot;
0816: this .domainZoomable = flag && (z.isDomainZoomable());
0817: }
0818: } else {
0819: this .domainZoomable = false;
0820: }
0821: }
0822:
0823: /**
0824: * Returns the flag that determines whether or not zooming is enabled for
0825: * the range axis.
0826: *
0827: * @return A boolean.
0828: */
0829: public boolean isRangeZoomable() {
0830: return this .rangeZoomable;
0831: }
0832:
0833: /**
0834: * A flag that controls mouse-based zooming on the vertical axis.
0835: *
0836: * @param flag <code>true</code> enables zooming.
0837: */
0838: public void setRangeZoomable(boolean flag) {
0839: if (flag) {
0840: Plot plot = this .chart.getPlot();
0841: if (plot instanceof Zoomable) {
0842: Zoomable z = (Zoomable) plot;
0843: this .rangeZoomable = flag && (z.isRangeZoomable());
0844: }
0845: } else {
0846: this .rangeZoomable = false;
0847: }
0848: }
0849:
0850: /**
0851: * Returns the flag that controls whether or not the zoom rectangle is
0852: * filled when drawn.
0853: *
0854: * @return A boolean.
0855: */
0856: public boolean getFillZoomRectangle() {
0857: return this .fillZoomRectangle;
0858: }
0859:
0860: /**
0861: * A flag that controls how the zoom rectangle is drawn.
0862: *
0863: * @param flag <code>true</code> instructs to fill the rectangle on
0864: * zoom, otherwise it will be outlined.
0865: */
0866: public void setFillZoomRectangle(boolean flag) {
0867: this .fillZoomRectangle = flag;
0868: }
0869:
0870: /**
0871: * Returns the zoom trigger distance. This controls how far the mouse must
0872: * move before a zoom action is triggered.
0873: *
0874: * @return The distance (in Java2D units).
0875: */
0876: public int getZoomTriggerDistance() {
0877: return this .zoomTriggerDistance;
0878: }
0879:
0880: /**
0881: * Sets the zoom trigger distance. This controls how far the mouse must
0882: * move before a zoom action is triggered.
0883: *
0884: * @param distance the distance (in Java2D units).
0885: */
0886: public void setZoomTriggerDistance(int distance) {
0887: this .zoomTriggerDistance = distance;
0888: }
0889:
0890: /**
0891: * Returns the flag that controls whether or not a horizontal axis trace
0892: * line is drawn over the plot area at the current mouse location.
0893: *
0894: * @return A boolean.
0895: */
0896: public boolean getHorizontalAxisTrace() {
0897: return this .horizontalAxisTrace;
0898: }
0899:
0900: /**
0901: * A flag that controls trace lines on the horizontal axis.
0902: *
0903: * @param flag <code>true</code> enables trace lines for the mouse
0904: * pointer on the horizontal axis.
0905: */
0906: public void setHorizontalAxisTrace(boolean flag) {
0907: this .horizontalAxisTrace = flag;
0908: }
0909:
0910: /**
0911: * Returns the horizontal trace line.
0912: *
0913: * @return The horizontal trace line (possibly <code>null</code>).
0914: */
0915: protected Line2D getHorizontalTraceLine() {
0916: return this .horizontalTraceLine;
0917: }
0918:
0919: /**
0920: * Sets the horizontal trace line.
0921: *
0922: * @param line the line (<code>null</code> permitted).
0923: */
0924: protected void setHorizontalTraceLine(Line2D line) {
0925: this .horizontalTraceLine = line;
0926: }
0927:
0928: /**
0929: * Returns the flag that controls whether or not a vertical axis trace
0930: * line is drawn over the plot area at the current mouse location.
0931: *
0932: * @return A boolean.
0933: */
0934: public boolean getVerticalAxisTrace() {
0935: return this .verticalAxisTrace;
0936: }
0937:
0938: /**
0939: * A flag that controls trace lines on the vertical axis.
0940: *
0941: * @param flag <code>true</code> enables trace lines for the mouse
0942: * pointer on the vertical axis.
0943: */
0944: public void setVerticalAxisTrace(boolean flag) {
0945: this .verticalAxisTrace = flag;
0946: }
0947:
0948: /**
0949: * Returns the vertical trace line.
0950: *
0951: * @return The vertical trace line (possibly <code>null</code>).
0952: */
0953: protected Line2D getVerticalTraceLine() {
0954: return this .verticalTraceLine;
0955: }
0956:
0957: /**
0958: * Sets the vertical trace line.
0959: *
0960: * @param line the line (<code>null</code> permitted).
0961: */
0962: protected void setVerticalTraceLine(Line2D line) {
0963: this .verticalTraceLine = line;
0964: }
0965:
0966: /**
0967: * Returns <code>true</code> if file extensions should be enforced, and
0968: * <code>false</code> otherwise.
0969: *
0970: * @return The flag.
0971: */
0972: public boolean isEnforceFileExtensions() {
0973: return this .enforceFileExtensions;
0974: }
0975:
0976: /**
0977: * Sets a flag that controls whether or not file extensions are enforced.
0978: *
0979: * @param enforce the new flag value.
0980: */
0981: public void setEnforceFileExtensions(boolean enforce) {
0982: this .enforceFileExtensions = enforce;
0983: }
0984:
0985: /**
0986: * Switches the display of tooltips for the panel on or off. Note that
0987: * tooltips can only be displayed if the chart has been configured to
0988: * generate tooltip items.
0989: *
0990: * @param flag <code>true</code> to enable tooltips, <code>false</code> to
0991: * disable tooltips.
0992: */
0993: public void setDisplayToolTips(boolean flag) {
0994: if (flag) {
0995: ToolTipManager.sharedInstance().registerComponent(this );
0996: } else {
0997: ToolTipManager.sharedInstance().unregisterComponent(this );
0998: }
0999: }
1000:
1001: /**
1002: * Returns a string for the tooltip.
1003: *
1004: * @param e the mouse event.
1005: *
1006: * @return A tool tip or <code>null</code> if no tooltip is available.
1007: */
1008: public String getToolTipText(MouseEvent e) {
1009:
1010: String result = null;
1011: if (this .info != null) {
1012: EntityCollection entities = this .info.getEntityCollection();
1013: if (entities != null) {
1014: Insets insets = getInsets();
1015: ChartEntity entity = entities.getEntity((int) ((e
1016: .getX() - insets.left) / this .scaleX),
1017: (int) ((e.getY() - insets.top) / this .scaleY));
1018: if (entity != null) {
1019: result = entity.getToolTipText();
1020: }
1021: }
1022: }
1023: return result;
1024:
1025: }
1026:
1027: /**
1028: * Translates a Java2D point on the chart to a screen location.
1029: *
1030: * @param java2DPoint the Java2D point.
1031: *
1032: * @return The screen location.
1033: */
1034: public Point translateJava2DToScreen(Point2D java2DPoint) {
1035: Insets insets = getInsets();
1036: int x = (int) (java2DPoint.getX() * this .scaleX + insets.left);
1037: int y = (int) (java2DPoint.getY() * this .scaleY + insets.top);
1038: return new Point(x, y);
1039: }
1040:
1041: /**
1042: * Translates a screen location to a Java2D point.
1043: *
1044: * @param screenPoint the screen location.
1045: *
1046: * @return The Java2D coordinates.
1047: */
1048: public Point2D translateScreenToJava2D(Point screenPoint) {
1049: Insets insets = getInsets();
1050: double x = (screenPoint.getX() - insets.left) / this .scaleX;
1051: double y = (screenPoint.getY() - insets.top) / this .scaleY;
1052: return new Point2D.Double(x, y);
1053: }
1054:
1055: /**
1056: * Applies any scaling that is in effect for the chart drawing to the
1057: * given rectangle.
1058: *
1059: * @param rect the rectangle.
1060: *
1061: * @return A new scaled rectangle.
1062: */
1063: public Rectangle2D scale(Rectangle2D rect) {
1064: Insets insets = getInsets();
1065: double x = rect.getX() * getScaleX() + insets.left;
1066: double y = rect.getY() * this .getScaleY() + insets.top;
1067: double w = rect.getWidth() * this .getScaleX();
1068: double h = rect.getHeight() * this .getScaleY();
1069: return new Rectangle2D.Double(x, y, w, h);
1070: }
1071:
1072: /**
1073: * Returns the chart entity at a given point.
1074: * <P>
1075: * This method will return null if there is (a) no entity at the given
1076: * point, or (b) no entity collection has been generated.
1077: *
1078: * @param viewX the x-coordinate.
1079: * @param viewY the y-coordinate.
1080: *
1081: * @return The chart entity (possibly <code>null</code>).
1082: */
1083: public ChartEntity getEntityForPoint(int viewX, int viewY) {
1084:
1085: ChartEntity result = null;
1086: if (this .info != null) {
1087: Insets insets = getInsets();
1088: double x = (viewX - insets.left) / this .scaleX;
1089: double y = (viewY - insets.top) / this .scaleY;
1090: EntityCollection entities = this .info.getEntityCollection();
1091: result = entities != null ? entities.getEntity(x, y) : null;
1092: }
1093: return result;
1094:
1095: }
1096:
1097: /**
1098: * Returns the flag that controls whether or not the offscreen buffer
1099: * needs to be refreshed.
1100: *
1101: * @return A boolean.
1102: */
1103: public boolean getRefreshBuffer() {
1104: return this .refreshBuffer;
1105: }
1106:
1107: /**
1108: * Sets the refresh buffer flag. This flag is used to avoid unnecessary
1109: * redrawing of the chart when the offscreen image buffer is used.
1110: *
1111: * @param flag <code>true</code> indicates that the buffer should be
1112: * refreshed.
1113: */
1114: public void setRefreshBuffer(boolean flag) {
1115: this .refreshBuffer = flag;
1116: }
1117:
1118: /**
1119: * Paints the component by drawing the chart to fill the entire component,
1120: * but allowing for the insets (which will be non-zero if a border has been
1121: * set for this component). To increase performance (at the expense of
1122: * memory), an off-screen buffer image can be used.
1123: *
1124: * @param g the graphics device for drawing on.
1125: */
1126: public void paintComponent(Graphics g) {
1127: super .paintComponent(g);
1128: if (this .chart == null) {
1129: return;
1130: }
1131: Graphics2D g2 = (Graphics2D) g.create();
1132:
1133: // first determine the size of the chart rendering area...
1134: Dimension size = getSize();
1135: Insets insets = getInsets();
1136: Rectangle2D available = new Rectangle2D.Double(insets.left,
1137: insets.top, size.getWidth() - insets.left
1138: - insets.right, size.getHeight() - insets.top
1139: - insets.bottom);
1140:
1141: // work out if scaling is required...
1142: boolean scale = false;
1143: double drawWidth = available.getWidth();
1144: double drawHeight = available.getHeight();
1145: this .scaleX = 1.0;
1146: this .scaleY = 1.0;
1147:
1148: if (drawWidth < this .minimumDrawWidth) {
1149: this .scaleX = drawWidth / this .minimumDrawWidth;
1150: drawWidth = this .minimumDrawWidth;
1151: scale = true;
1152: } else if (drawWidth > this .maximumDrawWidth) {
1153: this .scaleX = drawWidth / this .maximumDrawWidth;
1154: drawWidth = this .maximumDrawWidth;
1155: scale = true;
1156: }
1157:
1158: if (drawHeight < this .minimumDrawHeight) {
1159: this .scaleY = drawHeight / this .minimumDrawHeight;
1160: drawHeight = this .minimumDrawHeight;
1161: scale = true;
1162: } else if (drawHeight > this .maximumDrawHeight) {
1163: this .scaleY = drawHeight / this .maximumDrawHeight;
1164: drawHeight = this .maximumDrawHeight;
1165: scale = true;
1166: }
1167:
1168: Rectangle2D chartArea = new Rectangle2D.Double(0.0, 0.0,
1169: drawWidth, drawHeight);
1170:
1171: // are we using the chart buffer?
1172: if (this .useBuffer) {
1173:
1174: // do we need to resize the buffer?
1175: if ((this .chartBuffer == null)
1176: || (this .chartBufferWidth != available.getWidth())
1177: || (this .chartBufferHeight != available.getHeight())) {
1178: this .chartBufferWidth = (int) available.getWidth();
1179: this .chartBufferHeight = (int) available.getHeight();
1180: this .chartBuffer = createImage(this .chartBufferWidth,
1181: this .chartBufferHeight);
1182: // GraphicsConfiguration gc = g2.getDeviceConfiguration();
1183: // this.chartBuffer = gc.createCompatibleImage(
1184: // this.chartBufferWidth, this.chartBufferHeight,
1185: // Transparency.TRANSLUCENT);
1186: this .refreshBuffer = true;
1187: }
1188:
1189: // do we need to redraw the buffer?
1190: if (this .refreshBuffer) {
1191:
1192: Rectangle2D bufferArea = new Rectangle2D.Double(0, 0,
1193: this .chartBufferWidth, this .chartBufferHeight);
1194:
1195: Graphics2D bufferG2 = (Graphics2D) this .chartBuffer
1196: .getGraphics();
1197: if (scale) {
1198: AffineTransform saved = bufferG2.getTransform();
1199: AffineTransform st = AffineTransform
1200: .getScaleInstance(this .scaleX, this .scaleY);
1201: bufferG2.transform(st);
1202: this .chart.draw(bufferG2, chartArea, this .anchor,
1203: this .info);
1204: bufferG2.setTransform(saved);
1205: } else {
1206: this .chart.draw(bufferG2, bufferArea, this .anchor,
1207: this .info);
1208: }
1209:
1210: this .refreshBuffer = false;
1211:
1212: }
1213:
1214: // zap the buffer onto the panel...
1215: g2.drawImage(this .chartBuffer, insets.left, insets.top,
1216: this );
1217:
1218: }
1219:
1220: // or redrawing the chart every time...
1221: else {
1222:
1223: AffineTransform saved = g2.getTransform();
1224: g2.translate(insets.left, insets.top);
1225: if (scale) {
1226: AffineTransform st = AffineTransform.getScaleInstance(
1227: this .scaleX, this .scaleY);
1228: g2.transform(st);
1229: }
1230: this .chart.draw(g2, chartArea, this .anchor, this .info);
1231: g2.setTransform(saved);
1232:
1233: }
1234:
1235: // Redraw the zoom rectangle (if present)
1236: drawZoomRectangle(g2);
1237:
1238: g2.dispose();
1239:
1240: this .anchor = null;
1241: this .verticalTraceLine = null;
1242: this .horizontalTraceLine = null;
1243:
1244: }
1245:
1246: /**
1247: * Receives notification of changes to the chart, and redraws the chart.
1248: *
1249: * @param event details of the chart change event.
1250: */
1251: public void chartChanged(ChartChangeEvent event) {
1252: this .refreshBuffer = true;
1253: Plot plot = this .chart.getPlot();
1254: if (plot instanceof Zoomable) {
1255: Zoomable z = (Zoomable) plot;
1256: this .orientation = z.getOrientation();
1257: }
1258: repaint();
1259: }
1260:
1261: /**
1262: * Receives notification of a chart progress event.
1263: *
1264: * @param event the event.
1265: */
1266: public void chartProgress(ChartProgressEvent event) {
1267: // does nothing - override if necessary
1268: }
1269:
1270: /**
1271: * Handles action events generated by the popup menu.
1272: *
1273: * @param event the event.
1274: */
1275: public void actionPerformed(ActionEvent event) {
1276:
1277: String command = event.getActionCommand();
1278:
1279: // many of the zoom methods need a screen location - all we have is
1280: // the zoomPoint, but it might be null. Here we grab the x and y
1281: // coordinates, or use defaults...
1282: double screenX = -1.0;
1283: double screenY = -1.0;
1284: if (this .zoomPoint != null) {
1285: screenX = this .zoomPoint.getX();
1286: screenY = this .zoomPoint.getY();
1287: }
1288:
1289: if (command.equals(PROPERTIES_COMMAND)) {
1290: doEditChartProperties();
1291: } else if (command.equals(SAVE_COMMAND)) {
1292: try {
1293: doSaveAs();
1294: } catch (IOException e) {
1295: e.printStackTrace();
1296: }
1297: } else if (command.equals(PRINT_COMMAND)) {
1298: createChartPrintJob();
1299: } else if (command.equals(ZOOM_IN_BOTH_COMMAND)) {
1300: zoomInBoth(screenX, screenY);
1301: } else if (command.equals(ZOOM_IN_DOMAIN_COMMAND)) {
1302: zoomInDomain(screenX, screenY);
1303: } else if (command.equals(ZOOM_IN_RANGE_COMMAND)) {
1304: zoomInRange(screenX, screenY);
1305: } else if (command.equals(ZOOM_OUT_BOTH_COMMAND)) {
1306: zoomOutBoth(screenX, screenY);
1307: } else if (command.equals(ZOOM_OUT_DOMAIN_COMMAND)) {
1308: zoomOutDomain(screenX, screenY);
1309: } else if (command.equals(ZOOM_OUT_RANGE_COMMAND)) {
1310: zoomOutRange(screenX, screenY);
1311: } else if (command.equals(ZOOM_RESET_BOTH_COMMAND)) {
1312: restoreAutoBounds();
1313: } else if (command.equals(ZOOM_RESET_DOMAIN_COMMAND)) {
1314: restoreAutoDomainBounds();
1315: } else if (command.equals(ZOOM_RESET_RANGE_COMMAND)) {
1316: restoreAutoRangeBounds();
1317: }
1318:
1319: }
1320:
1321: /**
1322: * Handles a 'mouse entered' event. This method changes the tooltip delays
1323: * of ToolTipManager.sharedInstance() to the possibly different values set
1324: * for this chart panel.
1325: *
1326: * @param e the mouse event.
1327: */
1328: public void mouseEntered(MouseEvent e) {
1329: if (!this .ownToolTipDelaysActive) {
1330: ToolTipManager ttm = ToolTipManager.sharedInstance();
1331:
1332: this .originalToolTipInitialDelay = ttm.getInitialDelay();
1333: ttm.setInitialDelay(this .ownToolTipInitialDelay);
1334:
1335: this .originalToolTipReshowDelay = ttm.getReshowDelay();
1336: ttm.setReshowDelay(this .ownToolTipReshowDelay);
1337:
1338: this .originalToolTipDismissDelay = ttm.getDismissDelay();
1339: ttm.setDismissDelay(this .ownToolTipDismissDelay);
1340:
1341: this .ownToolTipDelaysActive = true;
1342: }
1343: }
1344:
1345: /**
1346: * Handles a 'mouse exited' event. This method resets the tooltip delays of
1347: * ToolTipManager.sharedInstance() to their
1348: * original values in effect before mouseEntered()
1349: *
1350: * @param e the mouse event.
1351: */
1352: public void mouseExited(MouseEvent e) {
1353: if (this .ownToolTipDelaysActive) {
1354: // restore original tooltip dealys
1355: ToolTipManager ttm = ToolTipManager.sharedInstance();
1356: ttm.setInitialDelay(this .originalToolTipInitialDelay);
1357: ttm.setReshowDelay(this .originalToolTipReshowDelay);
1358: ttm.setDismissDelay(this .originalToolTipDismissDelay);
1359: this .ownToolTipDelaysActive = false;
1360: }
1361: }
1362:
1363: /**
1364: * Handles a 'mouse pressed' event.
1365: * <P>
1366: * This event is the popup trigger on Unix/Linux. For Windows, the popup
1367: * trigger is the 'mouse released' event.
1368: *
1369: * @param e The mouse event.
1370: */
1371: public void mousePressed(MouseEvent e) {
1372: if (this .zoomRectangle == null) {
1373: Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e
1374: .getY());
1375: if (screenDataArea != null) {
1376: this .zoomPoint = getPointInRectangle(e.getX(),
1377: e.getY(), screenDataArea);
1378: } else {
1379: this .zoomPoint = null;
1380: }
1381: if (e.isPopupTrigger()) {
1382: if (this .popup != null) {
1383: displayPopupMenu(e.getX(), e.getY());
1384: }
1385: }
1386: }
1387: }
1388:
1389: /**
1390: * Returns a point based on (x, y) but constrained to be within the bounds
1391: * of the given rectangle. This method could be moved to JCommon.
1392: *
1393: * @param x the x-coordinate.
1394: * @param y the y-coordinate.
1395: * @param area the rectangle (<code>null</code> not permitted).
1396: *
1397: * @return A point within the rectangle.
1398: */
1399: private Point getPointInRectangle(int x, int y, Rectangle2D area) {
1400: x = (int) Math.max(Math.ceil(area.getMinX()), Math.min(x, Math
1401: .floor(area.getMaxX())));
1402: y = (int) Math.max(Math.ceil(area.getMinY()), Math.min(y, Math
1403: .floor(area.getMaxY())));
1404: return new Point(x, y);
1405: }
1406:
1407: /**
1408: * Handles a 'mouse dragged' event.
1409: *
1410: * @param e the mouse event.
1411: */
1412: public void mouseDragged(MouseEvent e) {
1413:
1414: // if the popup menu has already been triggered, then ignore dragging...
1415: if (this .popup != null && this .popup.isShowing()) {
1416: return;
1417: }
1418: // if no initial zoom point was set, ignore dragging...
1419: if (this .zoomPoint == null) {
1420: return;
1421: }
1422: Graphics2D g2 = (Graphics2D) getGraphics();
1423:
1424: // Erase the previous zoom rectangle (if any)...
1425: drawZoomRectangle(g2);
1426:
1427: boolean hZoom = false;
1428: boolean vZoom = false;
1429: if (this .orientation == PlotOrientation.HORIZONTAL) {
1430: hZoom = this .rangeZoomable;
1431: vZoom = this .domainZoomable;
1432: } else {
1433: hZoom = this .domainZoomable;
1434: vZoom = this .rangeZoomable;
1435: }
1436: Rectangle2D scaledDataArea = getScreenDataArea(
1437: (int) this .zoomPoint.getX(), (int) this .zoomPoint
1438: .getY());
1439: if (hZoom && vZoom) {
1440: // selected rectangle shouldn't extend outside the data area...
1441: double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
1442: double ymax = Math.min(e.getY(), scaledDataArea.getMaxY());
1443: this .zoomRectangle = new Rectangle2D.Double(this .zoomPoint
1444: .getX(), this .zoomPoint.getY(), xmax
1445: - this .zoomPoint.getX(), ymax
1446: - this .zoomPoint.getY());
1447: } else if (hZoom) {
1448: double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
1449: this .zoomRectangle = new Rectangle2D.Double(this .zoomPoint
1450: .getX(), scaledDataArea.getMinY(), xmax
1451: - this .zoomPoint.getX(), scaledDataArea.getHeight());
1452: } else if (vZoom) {
1453: double ymax = Math.min(e.getY(), scaledDataArea.getMaxY());
1454: this .zoomRectangle = new Rectangle2D.Double(scaledDataArea
1455: .getMinX(), this .zoomPoint.getY(), scaledDataArea
1456: .getWidth(), ymax - this .zoomPoint.getY());
1457: }
1458:
1459: // Draw the new zoom rectangle...
1460: drawZoomRectangle(g2);
1461:
1462: g2.dispose();
1463:
1464: }
1465:
1466: /**
1467: * Handles a 'mouse released' event. On Windows, we need to check if this
1468: * is a popup trigger, but only if we haven't already been tracking a zoom
1469: * rectangle.
1470: *
1471: * @param e information about the event.
1472: */
1473: public void mouseReleased(MouseEvent e) {
1474:
1475: if (this .zoomRectangle != null) {
1476: boolean hZoom = false;
1477: boolean vZoom = false;
1478: if (this .orientation == PlotOrientation.HORIZONTAL) {
1479: hZoom = this .rangeZoomable;
1480: vZoom = this .domainZoomable;
1481: } else {
1482: hZoom = this .domainZoomable;
1483: vZoom = this .rangeZoomable;
1484: }
1485:
1486: boolean zoomTrigger1 = hZoom
1487: && Math.abs(e.getX() - this .zoomPoint.getX()) >= this .zoomTriggerDistance;
1488: boolean zoomTrigger2 = vZoom
1489: && Math.abs(e.getY() - this .zoomPoint.getY()) >= this .zoomTriggerDistance;
1490: if (zoomTrigger1 || zoomTrigger2) {
1491: if ((hZoom && (e.getX() < this .zoomPoint.getX()))
1492: || (vZoom && (e.getY() < this .zoomPoint.getY()))) {
1493: restoreAutoBounds();
1494: } else {
1495: double x, y, w, h;
1496: Rectangle2D screenDataArea = getScreenDataArea(
1497: (int) this .zoomPoint.getX(),
1498: (int) this .zoomPoint.getY());
1499: // for mouseReleased event, (horizontalZoom || verticalZoom)
1500: // will be true, so we can just test for either being false;
1501: // otherwise both are true
1502: if (!vZoom) {
1503: x = this .zoomPoint.getX();
1504: y = screenDataArea.getMinY();
1505: w = Math.min(this .zoomRectangle.getWidth(),
1506: screenDataArea.getMaxX()
1507: - this .zoomPoint.getX());
1508: h = screenDataArea.getHeight();
1509: } else if (!hZoom) {
1510: x = screenDataArea.getMinX();
1511: y = this .zoomPoint.getY();
1512: w = screenDataArea.getWidth();
1513: h = Math.min(this .zoomRectangle.getHeight(),
1514: screenDataArea.getMaxY()
1515: - this .zoomPoint.getY());
1516: } else {
1517: x = this .zoomPoint.getX();
1518: y = this .zoomPoint.getY();
1519: w = Math.min(this .zoomRectangle.getWidth(),
1520: screenDataArea.getMaxX()
1521: - this .zoomPoint.getX());
1522: h = Math.min(this .zoomRectangle.getHeight(),
1523: screenDataArea.getMaxY()
1524: - this .zoomPoint.getY());
1525: }
1526: Rectangle2D zoomArea = new Rectangle2D.Double(x, y,
1527: w, h);
1528: zoom(zoomArea);
1529: }
1530: this .zoomPoint = null;
1531: this .zoomRectangle = null;
1532: } else {
1533: // Erase the zoom rectangle
1534: Graphics2D g2 = (Graphics2D) getGraphics();
1535: drawZoomRectangle(g2);
1536: g2.dispose();
1537: this .zoomPoint = null;
1538: this .zoomRectangle = null;
1539: }
1540:
1541: }
1542:
1543: else if (e.isPopupTrigger()) {
1544: if (this .popup != null) {
1545: displayPopupMenu(e.getX(), e.getY());
1546: }
1547: }
1548:
1549: }
1550:
1551: /**
1552: * Receives notification of mouse clicks on the panel. These are
1553: * translated and passed on to any registered chart mouse click listeners.
1554: *
1555: * @param event Information about the mouse event.
1556: */
1557: public void mouseClicked(MouseEvent event) {
1558:
1559: Insets insets = getInsets();
1560: int x = (int) ((event.getX() - insets.left) / this .scaleX);
1561: int y = (int) ((event.getY() - insets.top) / this .scaleY);
1562:
1563: this .anchor = new Point2D.Double(x, y);
1564: if (this .chart == null) {
1565: return;
1566: }
1567: this .chart.setNotify(true); // force a redraw
1568: // new entity code...
1569: Object[] listeners = this .chartMouseListeners
1570: .getListeners(ChartMouseListener.class);
1571: if (listeners.length == 0) {
1572: return;
1573: }
1574:
1575: ChartEntity entity = null;
1576: if (this .info != null) {
1577: EntityCollection entities = this .info.getEntityCollection();
1578: if (entities != null) {
1579: entity = entities.getEntity(x, y);
1580: }
1581: }
1582: ChartMouseEvent chartEvent = new ChartMouseEvent(getChart(),
1583: event, entity);
1584: for (int i = listeners.length - 1; i >= 0; i -= 1) {
1585: ((ChartMouseListener) listeners[i])
1586: .chartMouseClicked(chartEvent);
1587: }
1588:
1589: }
1590:
1591: /**
1592: * Implementation of the MouseMotionListener's method.
1593: *
1594: * @param e the event.
1595: */
1596: public void mouseMoved(MouseEvent e) {
1597: Graphics2D g2 = (Graphics2D) getGraphics();
1598: if (this .horizontalAxisTrace) {
1599: drawHorizontalAxisTrace(g2, e.getX());
1600: }
1601: if (this .verticalAxisTrace) {
1602: drawVerticalAxisTrace(g2, e.getY());
1603: }
1604: g2.dispose();
1605:
1606: Object[] listeners = this .chartMouseListeners
1607: .getListeners(ChartMouseListener.class);
1608: if (listeners.length == 0) {
1609: return;
1610: }
1611: Insets insets = getInsets();
1612: int x = (int) ((e.getX() - insets.left) / this .scaleX);
1613: int y = (int) ((e.getY() - insets.top) / this .scaleY);
1614:
1615: ChartEntity entity = null;
1616: if (this .info != null) {
1617: EntityCollection entities = this .info.getEntityCollection();
1618: if (entities != null) {
1619: entity = entities.getEntity(x, y);
1620: }
1621: }
1622:
1623: // we can only generate events if the panel's chart is not null
1624: // (see bug report 1556951)
1625: if (this .chart != null) {
1626: ChartMouseEvent event = new ChartMouseEvent(getChart(), e,
1627: entity);
1628: for (int i = listeners.length - 1; i >= 0; i -= 1) {
1629: ((ChartMouseListener) listeners[i])
1630: .chartMouseMoved(event);
1631: }
1632: }
1633:
1634: }
1635:
1636: /**
1637: * Zooms in on an anchor point (specified in screen coordinate space).
1638: *
1639: * @param x the x value (in screen coordinates).
1640: * @param y the y value (in screen coordinates).
1641: */
1642: public void zoomInBoth(double x, double y) {
1643: zoomInDomain(x, y);
1644: zoomInRange(x, y);
1645: }
1646:
1647: /**
1648: * Decreases the length of the domain axis, centered about the given
1649: * coordinate on the screen. The length of the domain axis is reduced
1650: * by the value of {@link #getZoomInFactor()}.
1651: *
1652: * @param x the x coordinate (in screen coordinates).
1653: * @param y the y-coordinate (in screen coordinates).
1654: */
1655: public void zoomInDomain(double x, double y) {
1656: Plot p = this .chart.getPlot();
1657: if (p instanceof Zoomable) {
1658: Zoomable plot = (Zoomable) p;
1659: plot.zoomDomainAxes(this .zoomInFactor, this .info
1660: .getPlotInfo(), translateScreenToJava2D(new Point(
1661: (int) x, (int) y)));
1662: }
1663: }
1664:
1665: /**
1666: * Decreases the length of the range axis, centered about the given
1667: * coordinate on the screen. The length of the range axis is reduced by
1668: * the value of {@link #getZoomInFactor()}.
1669: *
1670: * @param x the x-coordinate (in screen coordinates).
1671: * @param y the y coordinate (in screen coordinates).
1672: */
1673: public void zoomInRange(double x, double y) {
1674: Plot p = this .chart.getPlot();
1675: if (p instanceof Zoomable) {
1676: Zoomable z = (Zoomable) p;
1677: z
1678: .zoomRangeAxes(this .zoomInFactor, this .info
1679: .getPlotInfo(),
1680: translateScreenToJava2D(new Point((int) x,
1681: (int) y)));
1682: }
1683: }
1684:
1685: /**
1686: * Zooms out on an anchor point (specified in screen coordinate space).
1687: *
1688: * @param x the x value (in screen coordinates).
1689: * @param y the y value (in screen coordinates).
1690: */
1691: public void zoomOutBoth(double x, double y) {
1692: zoomOutDomain(x, y);
1693: zoomOutRange(x, y);
1694: }
1695:
1696: /**
1697: * Increases the length of the domain axis, centered about the given
1698: * coordinate on the screen. The length of the domain axis is increased
1699: * by the value of {@link #getZoomOutFactor()}.
1700: *
1701: * @param x the x coordinate (in screen coordinates).
1702: * @param y the y-coordinate (in screen coordinates).
1703: */
1704: public void zoomOutDomain(double x, double y) {
1705: Plot p = this .chart.getPlot();
1706: if (p instanceof Zoomable) {
1707: Zoomable z = (Zoomable) p;
1708: z.zoomDomainAxes(this .zoomOutFactor, this .info
1709: .getPlotInfo(), translateScreenToJava2D(new Point(
1710: (int) x, (int) y)));
1711: }
1712: }
1713:
1714: /**
1715: * Increases the length the range axis, centered about the given
1716: * coordinate on the screen. The length of the range axis is increased
1717: * by the value of {@link #getZoomOutFactor()}.
1718: *
1719: * @param x the x coordinate (in screen coordinates).
1720: * @param y the y-coordinate (in screen coordinates).
1721: */
1722: public void zoomOutRange(double x, double y) {
1723: Plot p = this .chart.getPlot();
1724: if (p instanceof Zoomable) {
1725: Zoomable z = (Zoomable) p;
1726: z
1727: .zoomRangeAxes(this .zoomOutFactor, this .info
1728: .getPlotInfo(),
1729: translateScreenToJava2D(new Point((int) x,
1730: (int) y)));
1731: }
1732: }
1733:
1734: /**
1735: * Zooms in on a selected region.
1736: *
1737: * @param selection the selected region.
1738: */
1739: public void zoom(Rectangle2D selection) {
1740:
1741: // get the origin of the zoom selection in the Java2D space used for
1742: // drawing the chart (that is, before any scaling to fit the panel)
1743: Point2D selectOrigin = translateScreenToJava2D(new Point(
1744: (int) Math.ceil(selection.getX()), (int) Math
1745: .ceil(selection.getY())));
1746: PlotRenderingInfo plotInfo = this .info.getPlotInfo();
1747: Rectangle2D scaledDataArea = getScreenDataArea((int) selection
1748: .getCenterX(), (int) selection.getCenterY());
1749: if ((selection.getHeight() > 0) && (selection.getWidth() > 0)) {
1750:
1751: double hLower = (selection.getMinX() - scaledDataArea
1752: .getMinX())
1753: / scaledDataArea.getWidth();
1754: double hUpper = (selection.getMaxX() - scaledDataArea
1755: .getMinX())
1756: / scaledDataArea.getWidth();
1757: double vLower = (scaledDataArea.getMaxY() - selection
1758: .getMaxY())
1759: / scaledDataArea.getHeight();
1760: double vUpper = (scaledDataArea.getMaxY() - selection
1761: .getMinY())
1762: / scaledDataArea.getHeight();
1763:
1764: Plot p = this .chart.getPlot();
1765: if (p instanceof Zoomable) {
1766: Zoomable z = (Zoomable) p;
1767: if (z.getOrientation() == PlotOrientation.HORIZONTAL) {
1768: z.zoomDomainAxes(vLower, vUpper, plotInfo,
1769: selectOrigin);
1770: z.zoomRangeAxes(hLower, hUpper, plotInfo,
1771: selectOrigin);
1772: } else {
1773: z.zoomDomainAxes(hLower, hUpper, plotInfo,
1774: selectOrigin);
1775: z.zoomRangeAxes(vLower, vUpper, plotInfo,
1776: selectOrigin);
1777: }
1778: }
1779:
1780: }
1781:
1782: }
1783:
1784: /**
1785: * Restores the auto-range calculation on both axes.
1786: */
1787: public void restoreAutoBounds() {
1788: restoreAutoDomainBounds();
1789: restoreAutoRangeBounds();
1790: }
1791:
1792: /**
1793: * Restores the auto-range calculation on the domain axis.
1794: */
1795: public void restoreAutoDomainBounds() {
1796: Plot p = this .chart.getPlot();
1797: if (p instanceof Zoomable) {
1798: Zoomable z = (Zoomable) p;
1799: // we need to guard against this.zoomPoint being null
1800: Point zp = (this .zoomPoint != null ? this .zoomPoint
1801: : new Point());
1802: z.zoomDomainAxes(0.0, this .info.getPlotInfo(), zp);
1803: }
1804: }
1805:
1806: /**
1807: * Restores the auto-range calculation on the range axis.
1808: */
1809: public void restoreAutoRangeBounds() {
1810: Plot p = this .chart.getPlot();
1811: if (p instanceof Zoomable) {
1812: Zoomable z = (Zoomable) p;
1813: // we need to guard against this.zoomPoint being null
1814: Point zp = (this .zoomPoint != null ? this .zoomPoint
1815: : new Point());
1816: z.zoomRangeAxes(0.0, this .info.getPlotInfo(), zp);
1817: }
1818: }
1819:
1820: /**
1821: * Returns the data area for the chart (the area inside the axes) with the
1822: * current scaling applied (that is, the area as it appears on screen).
1823: *
1824: * @return The scaled data area.
1825: */
1826: public Rectangle2D getScreenDataArea() {
1827: Rectangle2D dataArea = this .info.getPlotInfo().getDataArea();
1828: Insets insets = getInsets();
1829: double x = dataArea.getX() * this .scaleX + insets.left;
1830: double y = dataArea.getY() * this .scaleY + insets.top;
1831: double w = dataArea.getWidth() * this .scaleX;
1832: double h = dataArea.getHeight() * this .scaleY;
1833: return new Rectangle2D.Double(x, y, w, h);
1834: }
1835:
1836: /**
1837: * Returns the data area (the area inside the axes) for the plot or subplot,
1838: * with the current scaling applied.
1839: *
1840: * @param x the x-coordinate (for subplot selection).
1841: * @param y the y-coordinate (for subplot selection).
1842: *
1843: * @return The scaled data area.
1844: */
1845: public Rectangle2D getScreenDataArea(int x, int y) {
1846: PlotRenderingInfo plotInfo = this .info.getPlotInfo();
1847: Rectangle2D result;
1848: if (plotInfo.getSubplotCount() == 0) {
1849: result = getScreenDataArea();
1850: } else {
1851: // get the origin of the zoom selection in the Java2D space used for
1852: // drawing the chart (that is, before any scaling to fit the panel)
1853: Point2D selectOrigin = translateScreenToJava2D(new Point(x,
1854: y));
1855: int subplotIndex = plotInfo.getSubplotIndex(selectOrigin);
1856: if (subplotIndex == -1) {
1857: return null;
1858: }
1859: result = scale(plotInfo.getSubplotInfo(subplotIndex)
1860: .getDataArea());
1861: }
1862: return result;
1863: }
1864:
1865: /**
1866: * Returns the initial tooltip delay value used inside this chart panel.
1867: *
1868: * @return An integer representing the initial delay value, in milliseconds.
1869: *
1870: * @see javax.swing.ToolTipManager#getInitialDelay()
1871: */
1872: public int getInitialDelay() {
1873: return this .ownToolTipInitialDelay;
1874: }
1875:
1876: /**
1877: * Returns the reshow tooltip delay value used inside this chart panel.
1878: *
1879: * @return An integer representing the reshow delay value, in milliseconds.
1880: *
1881: * @see javax.swing.ToolTipManager#getReshowDelay()
1882: */
1883: public int getReshowDelay() {
1884: return this .ownToolTipReshowDelay;
1885: }
1886:
1887: /**
1888: * Returns the dismissal tooltip delay value used inside this chart panel.
1889: *
1890: * @return An integer representing the dismissal delay value, in
1891: * milliseconds.
1892: *
1893: * @see javax.swing.ToolTipManager#getDismissDelay()
1894: */
1895: public int getDismissDelay() {
1896: return this .ownToolTipDismissDelay;
1897: }
1898:
1899: /**
1900: * Specifies the initial delay value for this chart panel.
1901: *
1902: * @param delay the number of milliseconds to delay (after the cursor has
1903: * paused) before displaying.
1904: *
1905: * @see javax.swing.ToolTipManager#setInitialDelay(int)
1906: */
1907: public void setInitialDelay(int delay) {
1908: this .ownToolTipInitialDelay = delay;
1909: }
1910:
1911: /**
1912: * Specifies the amount of time before the user has to wait initialDelay
1913: * milliseconds before a tooltip will be shown.
1914: *
1915: * @param delay time in milliseconds
1916: *
1917: * @see javax.swing.ToolTipManager#setReshowDelay(int)
1918: */
1919: public void setReshowDelay(int delay) {
1920: this .ownToolTipReshowDelay = delay;
1921: }
1922:
1923: /**
1924: * Specifies the dismissal delay value for this chart panel.
1925: *
1926: * @param delay the number of milliseconds to delay before taking away the
1927: * tooltip
1928: *
1929: * @see javax.swing.ToolTipManager#setDismissDelay(int)
1930: */
1931: public void setDismissDelay(int delay) {
1932: this .ownToolTipDismissDelay = delay;
1933: }
1934:
1935: /**
1936: * Returns the zoom in factor.
1937: *
1938: * @return The zoom in factor.
1939: *
1940: * @see #setZoomInFactor(double)
1941: */
1942: public double getZoomInFactor() {
1943: return this .zoomInFactor;
1944: }
1945:
1946: /**
1947: * Sets the zoom in factor.
1948: *
1949: * @param factor the factor.
1950: *
1951: * @see #getZoomInFactor()
1952: */
1953: public void setZoomInFactor(double factor) {
1954: this .zoomInFactor = factor;
1955: }
1956:
1957: /**
1958: * Returns the zoom out factor.
1959: *
1960: * @return The zoom out factor.
1961: *
1962: * @see #setZoomOutFactor(double)
1963: */
1964: public double getZoomOutFactor() {
1965: return this .zoomOutFactor;
1966: }
1967:
1968: /**
1969: * Sets the zoom out factor.
1970: *
1971: * @param factor the factor.
1972: *
1973: * @see #getZoomOutFactor()
1974: */
1975: public void setZoomOutFactor(double factor) {
1976: this .zoomOutFactor = factor;
1977: }
1978:
1979: /**
1980: * Draws zoom rectangle (if present).
1981: * The drawing is performed in XOR mode, therefore
1982: * when this method is called twice in a row,
1983: * the second call will completely restore the state
1984: * of the canvas.
1985: *
1986: * @param g2 the graphics device.
1987: */
1988: private void drawZoomRectangle(Graphics2D g2) {
1989: // Set XOR mode to draw the zoom rectangle
1990: g2.setXORMode(Color.gray);
1991: if (this .zoomRectangle != null) {
1992: if (this .fillZoomRectangle) {
1993: g2.fill(this .zoomRectangle);
1994: } else {
1995: g2.draw(this .zoomRectangle);
1996: }
1997: }
1998: // Reset to the default 'overwrite' mode
1999: g2.setPaintMode();
2000: }
2001:
2002: /**
2003: * Draws a vertical line used to trace the mouse position to the horizontal
2004: * axis.
2005: *
2006: * @param g2 the graphics device.
2007: * @param x the x-coordinate of the trace line.
2008: */
2009: private void drawHorizontalAxisTrace(Graphics2D g2, int x) {
2010:
2011: Rectangle2D dataArea = getScreenDataArea();
2012:
2013: g2.setXORMode(Color.orange);
2014: if (((int) dataArea.getMinX() < x)
2015: && (x < (int) dataArea.getMaxX())) {
2016:
2017: if (this .verticalTraceLine != null) {
2018: g2.draw(this .verticalTraceLine);
2019: this .verticalTraceLine.setLine(x, (int) dataArea
2020: .getMinY(), x, (int) dataArea.getMaxY());
2021: } else {
2022: this .verticalTraceLine = new Line2D.Float(x,
2023: (int) dataArea.getMinY(), x, (int) dataArea
2024: .getMaxY());
2025: }
2026: g2.draw(this .verticalTraceLine);
2027: }
2028:
2029: // Reset to the default 'overwrite' mode
2030: g2.setPaintMode();
2031: }
2032:
2033: /**
2034: * Draws a horizontal line used to trace the mouse position to the vertical
2035: * axis.
2036: *
2037: * @param g2 the graphics device.
2038: * @param y the y-coordinate of the trace line.
2039: */
2040: private void drawVerticalAxisTrace(Graphics2D g2, int y) {
2041:
2042: Rectangle2D dataArea = getScreenDataArea();
2043:
2044: g2.setXORMode(Color.orange);
2045: if (((int) dataArea.getMinY() < y)
2046: && (y < (int) dataArea.getMaxY())) {
2047:
2048: if (this .horizontalTraceLine != null) {
2049: g2.draw(this .horizontalTraceLine);
2050: this .horizontalTraceLine.setLine((int) dataArea
2051: .getMinX(), y, (int) dataArea.getMaxX(), y);
2052: } else {
2053: this .horizontalTraceLine = new Line2D.Float(
2054: (int) dataArea.getMinX(), y, (int) dataArea
2055: .getMaxX(), y);
2056: }
2057: g2.draw(this .horizontalTraceLine);
2058: }
2059:
2060: // Reset to the default 'overwrite' mode
2061: g2.setPaintMode();
2062: }
2063:
2064: /**
2065: * Displays a dialog that allows the user to edit the properties for the
2066: * current chart.
2067: *
2068: * @since 1.0.3
2069: */
2070: public void doEditChartProperties() {
2071:
2072: ChartEditor editor = ChartEditorManager
2073: .getChartEditor(this .chart);
2074: int result = JOptionPane
2075: .showConfirmDialog(this , editor, localizationResources
2076: .getString("Chart_Properties"),
2077: JOptionPane.OK_CANCEL_OPTION,
2078: JOptionPane.PLAIN_MESSAGE);
2079: if (result == JOptionPane.OK_OPTION) {
2080: editor.updateChart(this .chart);
2081: }
2082:
2083: }
2084:
2085: /**
2086: * Opens a file chooser and gives the user an opportunity to save the chart
2087: * in PNG format.
2088: *
2089: * @throws IOException if there is an I/O error.
2090: */
2091: public void doSaveAs() throws IOException {
2092:
2093: JFileChooser fileChooser = new JFileChooser();
2094: ExtensionFileFilter filter = new ExtensionFileFilter(
2095: localizationResources.getString("PNG_Image_Files"),
2096: ".png");
2097: fileChooser.addChoosableFileFilter(filter);
2098:
2099: int option = fileChooser.showSaveDialog(this );
2100: if (option == JFileChooser.APPROVE_OPTION) {
2101: String filename = fileChooser.getSelectedFile().getPath();
2102: if (isEnforceFileExtensions()) {
2103: if (!filename.endsWith(".png")) {
2104: filename = filename + ".png";
2105: }
2106: }
2107: ChartUtilities.saveChartAsPNG(new File(filename),
2108: this .chart, getWidth(), getHeight());
2109: }
2110:
2111: }
2112:
2113: /**
2114: * Creates a print job for the chart.
2115: */
2116: public void createChartPrintJob() {
2117:
2118: PrinterJob job = PrinterJob.getPrinterJob();
2119: PageFormat pf = job.defaultPage();
2120: PageFormat pf2 = job.pageDialog(pf);
2121: if (pf2 != pf) {
2122: job.setPrintable(this , pf2);
2123: if (job.printDialog()) {
2124: try {
2125: job.print();
2126: } catch (PrinterException e) {
2127: JOptionPane.showMessageDialog(this , e);
2128: }
2129: }
2130: }
2131:
2132: }
2133:
2134: /**
2135: * Prints the chart on a single page.
2136: *
2137: * @param g the graphics context.
2138: * @param pf the page format to use.
2139: * @param pageIndex the index of the page. If not <code>0</code>, nothing
2140: * gets print.
2141: *
2142: * @return The result of printing.
2143: */
2144: public int print(Graphics g, PageFormat pf, int pageIndex) {
2145:
2146: if (pageIndex != 0) {
2147: return NO_SUCH_PAGE;
2148: }
2149: Graphics2D g2 = (Graphics2D) g;
2150: double x = pf.getImageableX();
2151: double y = pf.getImageableY();
2152: double w = pf.getImageableWidth();
2153: double h = pf.getImageableHeight();
2154: this .chart.draw(g2, new Rectangle2D.Double(x, y, w, h),
2155: this .anchor, null);
2156: return PAGE_EXISTS;
2157:
2158: }
2159:
2160: /**
2161: * Adds a listener to the list of objects listening for chart mouse events.
2162: *
2163: * @param listener the listener (<code>null</code> not permitted).
2164: */
2165: public void addChartMouseListener(ChartMouseListener listener) {
2166: if (listener == null) {
2167: throw new IllegalArgumentException(
2168: "Null 'listener' argument.");
2169: }
2170: this .chartMouseListeners
2171: .add(ChartMouseListener.class, listener);
2172: }
2173:
2174: /**
2175: * Removes a listener from the list of objects listening for chart mouse
2176: * events.
2177: *
2178: * @param listener the listener.
2179: */
2180: public void removeChartMouseListener(ChartMouseListener listener) {
2181: this .chartMouseListeners.remove(ChartMouseListener.class,
2182: listener);
2183: }
2184:
2185: /**
2186: * Returns an array of the listeners of the given type registered with the
2187: * panel.
2188: *
2189: * @param listenerType the listener type.
2190: *
2191: * @return An array of listeners.
2192: */
2193: public EventListener[] getListeners(Class listenerType) {
2194: if (listenerType == ChartMouseListener.class) {
2195: // fetch listeners from local storage
2196: return this .chartMouseListeners.getListeners(listenerType);
2197: } else {
2198: return super .getListeners(listenerType);
2199: }
2200: }
2201:
2202: /**
2203: * Creates a popup menu for the panel.
2204: *
2205: * @param properties include a menu item for the chart property editor.
2206: * @param save include a menu item for saving the chart.
2207: * @param print include a menu item for printing the chart.
2208: * @param zoom include menu items for zooming.
2209: *
2210: * @return The popup menu.
2211: */
2212: protected JPopupMenu createPopupMenu(boolean properties,
2213: boolean save, boolean print, boolean zoom) {
2214:
2215: JPopupMenu result = new JPopupMenu("Chart:");
2216: boolean separator = false;
2217:
2218: if (properties) {
2219: JMenuItem propertiesItem = new JMenuItem(
2220: localizationResources.getString("Properties..."));
2221: propertiesItem.setActionCommand(PROPERTIES_COMMAND);
2222: propertiesItem.addActionListener(this );
2223: result.add(propertiesItem);
2224: separator = true;
2225: }
2226:
2227: if (save) {
2228: if (separator) {
2229: result.addSeparator();
2230: separator = false;
2231: }
2232: JMenuItem saveItem = new JMenuItem(localizationResources
2233: .getString("Save_as..."));
2234: saveItem.setActionCommand(SAVE_COMMAND);
2235: saveItem.addActionListener(this );
2236: result.add(saveItem);
2237: separator = true;
2238: }
2239:
2240: if (print) {
2241: if (separator) {
2242: result.addSeparator();
2243: separator = false;
2244: }
2245: JMenuItem printItem = new JMenuItem(localizationResources
2246: .getString("Print..."));
2247: printItem.setActionCommand(PRINT_COMMAND);
2248: printItem.addActionListener(this );
2249: result.add(printItem);
2250: separator = true;
2251: }
2252:
2253: if (zoom) {
2254: if (separator) {
2255: result.addSeparator();
2256: separator = false;
2257: }
2258:
2259: JMenu zoomInMenu = new JMenu(localizationResources
2260: .getString("Zoom_In"));
2261:
2262: this .zoomInBothMenuItem = new JMenuItem(
2263: localizationResources.getString("All_Axes"));
2264: this .zoomInBothMenuItem
2265: .setActionCommand(ZOOM_IN_BOTH_COMMAND);
2266: this .zoomInBothMenuItem.addActionListener(this );
2267: zoomInMenu.add(this .zoomInBothMenuItem);
2268:
2269: zoomInMenu.addSeparator();
2270:
2271: this .zoomInDomainMenuItem = new JMenuItem(
2272: localizationResources.getString("Domain_Axis"));
2273: this .zoomInDomainMenuItem
2274: .setActionCommand(ZOOM_IN_DOMAIN_COMMAND);
2275: this .zoomInDomainMenuItem.addActionListener(this );
2276: zoomInMenu.add(this .zoomInDomainMenuItem);
2277:
2278: this .zoomInRangeMenuItem = new JMenuItem(
2279: localizationResources.getString("Range_Axis"));
2280: this .zoomInRangeMenuItem
2281: .setActionCommand(ZOOM_IN_RANGE_COMMAND);
2282: this .zoomInRangeMenuItem.addActionListener(this );
2283: zoomInMenu.add(this .zoomInRangeMenuItem);
2284:
2285: result.add(zoomInMenu);
2286:
2287: JMenu zoomOutMenu = new JMenu(localizationResources
2288: .getString("Zoom_Out"));
2289:
2290: this .zoomOutBothMenuItem = new JMenuItem(
2291: localizationResources.getString("All_Axes"));
2292: this .zoomOutBothMenuItem
2293: .setActionCommand(ZOOM_OUT_BOTH_COMMAND);
2294: this .zoomOutBothMenuItem.addActionListener(this );
2295: zoomOutMenu.add(this .zoomOutBothMenuItem);
2296:
2297: zoomOutMenu.addSeparator();
2298:
2299: this .zoomOutDomainMenuItem = new JMenuItem(
2300: localizationResources.getString("Domain_Axis"));
2301: this .zoomOutDomainMenuItem
2302: .setActionCommand(ZOOM_OUT_DOMAIN_COMMAND);
2303: this .zoomOutDomainMenuItem.addActionListener(this );
2304: zoomOutMenu.add(this .zoomOutDomainMenuItem);
2305:
2306: this .zoomOutRangeMenuItem = new JMenuItem(
2307: localizationResources.getString("Range_Axis"));
2308: this .zoomOutRangeMenuItem
2309: .setActionCommand(ZOOM_OUT_RANGE_COMMAND);
2310: this .zoomOutRangeMenuItem.addActionListener(this );
2311: zoomOutMenu.add(this .zoomOutRangeMenuItem);
2312:
2313: result.add(zoomOutMenu);
2314:
2315: JMenu autoRangeMenu = new JMenu(localizationResources
2316: .getString("Auto_Range"));
2317:
2318: this .zoomResetBothMenuItem = new JMenuItem(
2319: localizationResources.getString("All_Axes"));
2320: this .zoomResetBothMenuItem
2321: .setActionCommand(ZOOM_RESET_BOTH_COMMAND);
2322: this .zoomResetBothMenuItem.addActionListener(this );
2323: autoRangeMenu.add(this .zoomResetBothMenuItem);
2324:
2325: autoRangeMenu.addSeparator();
2326: this .zoomResetDomainMenuItem = new JMenuItem(
2327: localizationResources.getString("Domain_Axis"));
2328: this .zoomResetDomainMenuItem
2329: .setActionCommand(ZOOM_RESET_DOMAIN_COMMAND);
2330: this .zoomResetDomainMenuItem.addActionListener(this );
2331: autoRangeMenu.add(this .zoomResetDomainMenuItem);
2332:
2333: this .zoomResetRangeMenuItem = new JMenuItem(
2334: localizationResources.getString("Range_Axis"));
2335: this .zoomResetRangeMenuItem
2336: .setActionCommand(ZOOM_RESET_RANGE_COMMAND);
2337: this .zoomResetRangeMenuItem.addActionListener(this );
2338: autoRangeMenu.add(this .zoomResetRangeMenuItem);
2339:
2340: result.addSeparator();
2341: result.add(autoRangeMenu);
2342:
2343: }
2344:
2345: return result;
2346:
2347: }
2348:
2349: /**
2350: * The idea is to modify the zooming options depending on the type of chart
2351: * being displayed by the panel.
2352: *
2353: * @param x horizontal position of the popup.
2354: * @param y vertical position of the popup.
2355: */
2356: protected void displayPopupMenu(int x, int y) {
2357:
2358: if (this .popup != null) {
2359:
2360: // go through each zoom menu item and decide whether or not to
2361: // enable it...
2362: Plot plot = this .chart.getPlot();
2363: boolean isDomainZoomable = false;
2364: boolean isRangeZoomable = false;
2365: if (plot instanceof Zoomable) {
2366: Zoomable z = (Zoomable) plot;
2367: isDomainZoomable = z.isDomainZoomable();
2368: isRangeZoomable = z.isRangeZoomable();
2369: }
2370:
2371: if (this .zoomInDomainMenuItem != null) {
2372: this .zoomInDomainMenuItem.setEnabled(isDomainZoomable);
2373: }
2374: if (this .zoomOutDomainMenuItem != null) {
2375: this .zoomOutDomainMenuItem.setEnabled(isDomainZoomable);
2376: }
2377: if (this .zoomResetDomainMenuItem != null) {
2378: this .zoomResetDomainMenuItem
2379: .setEnabled(isDomainZoomable);
2380: }
2381:
2382: if (this .zoomInRangeMenuItem != null) {
2383: this .zoomInRangeMenuItem.setEnabled(isRangeZoomable);
2384: }
2385: if (this .zoomOutRangeMenuItem != null) {
2386: this .zoomOutRangeMenuItem.setEnabled(isRangeZoomable);
2387: }
2388:
2389: if (this .zoomResetRangeMenuItem != null) {
2390: this .zoomResetRangeMenuItem.setEnabled(isRangeZoomable);
2391: }
2392:
2393: if (this .zoomInBothMenuItem != null) {
2394: this .zoomInBothMenuItem.setEnabled(isDomainZoomable
2395: && isRangeZoomable);
2396: }
2397: if (this .zoomOutBothMenuItem != null) {
2398: this .zoomOutBothMenuItem.setEnabled(isDomainZoomable
2399: && isRangeZoomable);
2400: }
2401: if (this .zoomResetBothMenuItem != null) {
2402: this .zoomResetBothMenuItem.setEnabled(isDomainZoomable
2403: && isRangeZoomable);
2404: }
2405:
2406: this .popup.show(this , x, y);
2407: }
2408:
2409: }
2410:
2411: /* (non-Javadoc)
2412: * @see javax.swing.JPanel#updateUI()
2413: */
2414: public void updateUI() {
2415: // here we need to update the UI for the popup menu, if the panel
2416: // has one...
2417: if (this.popup != null) {
2418: SwingUtilities.updateComponentTreeUI(this.popup);
2419: }
2420: super.updateUI();
2421: }
2422:
2423: }
|