Source for org.jfree.chart.ChartPanel

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
  22:  * USA.
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------------
  28:  * ChartPanel.java
  29:  * ---------------
  30:  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Andrzej Porebski;
  34:  *                   Soren Caspersen;
  35:  *                   Jonathan Nash;
  36:  *                   Hans-Jurgen Greiner;
  37:  *                   Andreas Schneider;
  38:  *                   Daniel van Enckevort;
  39:  *                   David M O'Donnell;
  40:  *                   Arnaud Lelievre;
  41:  *                   Matthias Rose;
  42:  *                   Onno vd Akker;
  43:  *                   Sergei Ivanov;
  44:  *
  45:  * Changes (from 28-Jun-2001)
  46:  * --------------------------
  47:  * 28-Jun-2001 : Integrated buffering code contributed by S???ren
  48:  *               Caspersen (DG);
  49:  * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
  50:  * 22-Nov-2001 : Added scaling to improve display of charts in small sizes (DG);
  51:  * 26-Nov-2001 : Added property editing, saving and printing (DG);
  52:  * 11-Dec-2001 : Transferred saveChartAsPNG method to new ChartUtilities
  53:  *               class (DG);
  54:  * 13-Dec-2001 : Added tooltips (DG);
  55:  * 16-Jan-2002 : Added an optional crosshair, based on the implementation by
  56:  *               Jonathan Nash. Renamed the tooltips class (DG);
  57:  * 23-Jan-2002 : Implemented zooming based on code by Hans-Jurgen Greiner (DG);
  58:  * 05-Feb-2002 : Improved tooltips setup.  Renamed method attemptSaveAs()
  59:  *               --> doSaveAs() and made it public rather than private (DG);
  60:  * 28-Mar-2002 : Added a new constructor (DG);
  61:  * 09-Apr-2002 : Changed initialisation of tooltip generation, as suggested by
  62:  *               Hans-Jurgen Greiner (DG);
  63:  * 27-May-2002 : New interactive zooming methods based on code by Hans-Jurgen
  64:  *               Greiner. Renamed JFreeChartPanel --> ChartPanel, moved
  65:  *               constants to ChartPanelConstants interface (DG);
  66:  * 31-May-2002 : Fixed a bug with interactive zooming and added a way to
  67:  *               control if the zoom rectangle is filled in or drawn as an
  68:  *               outline. A mouse drag gesture towards the top left now causes
  69:  *               an autoRangeBoth() and is a way to undo zooms (AS);
  70:  * 11-Jun-2002 : Reinstated handleClick method call in mouseClicked() to get
  71:  *               crosshairs working again (DG);
  72:  * 13-Jun-2002 : Added check for null popup menu in mouseDragged method (DG);
  73:  * 18-Jun-2002 : Added get/set methods for minimum and maximum chart
  74:  *               dimensions (DG);
  75:  * 25-Jun-2002 : Removed redundant code (DG);
  76:  * 27-Aug-2002 : Added get/set methods for popup menu (DG);
  77:  * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
  78:  * 22-Oct-2002 : Added translation methods for screen <--> Java2D, contributed
  79:  *               by Daniel van Enckevort (DG);
  80:  * 05-Nov-2002 : Added a chart reference to the ChartMouseEvent class (DG);
  81:  * 22-Nov-2002 : Added test in zoom method for inverted axes, supplied by
  82:  *               David M O'Donnell (DG);
  83:  * 14-Jan-2003 : Implemented ChartProgressListener interface (DG);
  84:  * 14-Feb-2003 : Removed deprecated setGenerateTooltips method (DG);
  85:  * 12-Mar-2003 : Added option to enforce filename extension (see bug id
  86:  *               643173) (DG);
  87:  * 08-Sep-2003 : Added internationalization via use of properties
  88:  *               resourceBundle (RFE 690236) (AL);
  89:  * 18-Sep-2003 : Added getScaleX() and getScaleY() methods (protected) as
  90:  *               requested by Irv Thomae (DG);
  91:  * 12-Nov-2003 : Added zooming support for the FastScatterPlot class (DG);
  92:  * 24-Nov-2003 : Minor Javadoc updates (DG);
  93:  * 04-Dec-2003 : Added anchor point for crosshair calculation (DG);
  94:  * 17-Jan-2004 : Added new methods to set tooltip delays to be used in this
  95:  *               chart panel. Refer to patch 877565 (MR);
  96:  * 02-Feb-2004 : Fixed bug in zooming trigger and added zoomTriggerDistance
  97:  *               attribute (DG);
  98:  * 08-Apr-2004 : Changed getScaleX() and getScaleY() from protected to
  99:  *               public (DG);
 100:  * 15-Apr-2004 : Added zoomOutFactor and zoomInFactor (DG);
 101:  * 21-Apr-2004 : Fixed zooming bug in mouseReleased() method (DG);
 102:  * 13-Jul-2004 : Added check for null chart (DG);
 103:  * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
 104:  * 11-Nov-2004 : Moved constants back in from ChartPanelConstants (DG);
 105:  * 12-Nov-2004 : Modified zooming mechanism to support zooming within
 106:  *               subplots (DG);
 107:  * 26-Jan-2005 : Fixed mouse zooming for horizontal category plots (DG);
 108:  * 11-Apr-2005 : Added getFillZoomRectangle() method, renamed
 109:  *               setHorizontalZoom() --> setDomainZoomable(),
 110:  *               setVerticalZoom() --> setRangeZoomable(), added
 111:  *               isDomainZoomable() and isRangeZoomable(), added
 112:  *               getHorizontalAxisTrace() and getVerticalAxisTrace(),
 113:  *               renamed autoRangeBoth() --> restoreAutoBounds(),
 114:  *               autoRangeHorizontal() --> restoreAutoDomainBounds(),
 115:  *               autoRangeVertical() --> restoreAutoRangeBounds() (DG);
 116:  * 12-Apr-2005 : Removed working areas, added getAnchorPoint() method,
 117:  *               added protected accessors for tracelines (DG);
 118:  * 18-Apr-2005 : Made constants final (DG);
 119:  * 26-Apr-2005 : Removed LOGGER (DG);
 120:  * 01-Jun-2005 : Fixed zooming for combined plots - see bug report
 121:  *               1212039, fix thanks to Onno vd Akker (DG);
 122:  * 25-Nov-2005 : Reworked event listener mechanism (DG);
 123:  * ------------- JFREECHART 1.0.x ---------------------------------------------
 124:  * 01-Aug-2006 : Fixed minor bug in restoreAutoRangeBounds() (DG);
 125:  * 04-Sep-2006 : Renamed attemptEditChartProperties() -->
 126:  *               doEditChartProperties() and made public (DG);
 127:  * 13-Sep-2006 : Don't generate ChartMouseEvents if the panel's chart is null
 128:  *               (fixes bug 1556951) (DG);
 129:  * 05-Mar-2007 : Applied patch 1672561 by Sergei Ivanov, to fix zoom rectangle
 130:  *               drawing for dynamic charts (DG);
 131:  * 17-Apr-2007 : Fix NullPointerExceptions in zooming for combined plots (DG);
 132:  * 24-May-2007 : When the look-and-feel changes, update the popup menu if there
 133:  *               is one (DG);
 134:  * 06-Jun-2007 : Fixed coordinates for drawing buffer image (DG);
 135:  * 24-Sep-2007 : Added zoomAroundAnchor flag, and handle clearing of chart
 136:  *               buffer (DG);
 137:  * 25-Oct-2007 : Added default directory attribute (DG);
 138:  * 07-Nov-2007 : Fixed (rare) bug in refreshing off-screen image (DG);
 139:  * 07-May-2008 : Fixed bug in zooming that triggered zoom for a rectangle
 140:  *               outside of the data area (DG);
 141:  * 08-May-2008 : Fixed serialization bug (DG);
 142:  *
 143:  */
 144: 
 145: package org.jfree.chart;
 146: 
 147: import java.awt.AWTEvent;
 148: import java.awt.Color;
 149: import java.awt.Dimension;
 150: import java.awt.Graphics;
 151: import java.awt.Graphics2D;
 152: import java.awt.Image;
 153: import java.awt.Insets;
 154: import java.awt.Point;
 155: import java.awt.event.ActionEvent;
 156: import java.awt.event.ActionListener;
 157: import java.awt.event.MouseEvent;
 158: import java.awt.event.MouseListener;
 159: import java.awt.event.MouseMotionListener;
 160: import java.awt.geom.AffineTransform;
 161: import java.awt.geom.Line2D;
 162: import java.awt.geom.Point2D;
 163: import java.awt.geom.Rectangle2D;
 164: import java.awt.print.PageFormat;
 165: import java.awt.print.Printable;
 166: import java.awt.print.PrinterException;
 167: import java.awt.print.PrinterJob;
 168: import java.io.File;
 169: import java.io.IOException;
 170: import java.io.ObjectInputStream;
 171: import java.io.ObjectOutputStream;
 172: import java.io.Serializable;
 173: import java.util.EventListener;
 174: import java.util.ResourceBundle;
 175: 
 176: import javax.swing.JFileChooser;
 177: import javax.swing.JMenu;
 178: import javax.swing.JMenuItem;
 179: import javax.swing.JOptionPane;
 180: import javax.swing.JPanel;
 181: import javax.swing.JPopupMenu;
 182: import javax.swing.SwingUtilities;
 183: import javax.swing.ToolTipManager;
 184: import javax.swing.event.EventListenerList;
 185: 
 186: import org.jfree.chart.editor.ChartEditor;
 187: import org.jfree.chart.editor.ChartEditorManager;
 188: import org.jfree.chart.entity.ChartEntity;
 189: import org.jfree.chart.entity.EntityCollection;
 190: import org.jfree.chart.event.ChartChangeEvent;
 191: import org.jfree.chart.event.ChartChangeListener;
 192: import org.jfree.chart.event.ChartProgressEvent;
 193: import org.jfree.chart.event.ChartProgressListener;
 194: import org.jfree.chart.plot.Plot;
 195: import org.jfree.chart.plot.PlotOrientation;
 196: import org.jfree.chart.plot.PlotRenderingInfo;
 197: import org.jfree.chart.plot.Zoomable;
 198: import org.jfree.ui.ExtensionFileFilter;
 199: 
 200: /**
 201:  * A Swing GUI component for displaying a {@link JFreeChart} object.
 202:  * <P>
 203:  * The panel registers with the chart to receive notification of changes to any
 204:  * component of the chart.  The chart is redrawn automatically whenever this
 205:  * notification is received.
 206:  */
 207: public class ChartPanel extends JPanel implements ChartChangeListener,
 208:         ChartProgressListener, ActionListener, MouseListener,
 209:         MouseMotionListener, Printable, Serializable {
 210: 
 211:     /** For serialization. */
 212:     private static final long serialVersionUID = 6046366297214274674L;
 213: 
 214:     /** Default setting for buffer usage. */
 215:     public static final boolean DEFAULT_BUFFER_USED = false;
 216: 
 217:     /** The default panel width. */
 218:     public static final int DEFAULT_WIDTH = 680;
 219: 
 220:     /** The default panel height. */
 221:     public static final int DEFAULT_HEIGHT = 420;
 222: 
 223:     /** The default limit below which chart scaling kicks in. */
 224:     public static final int DEFAULT_MINIMUM_DRAW_WIDTH = 300;
 225: 
 226:     /** The default limit below which chart scaling kicks in. */
 227:     public static final int DEFAULT_MINIMUM_DRAW_HEIGHT = 200;
 228: 
 229:     /** The default limit below which chart scaling kicks in. */
 230:     public static final int DEFAULT_MAXIMUM_DRAW_WIDTH = 800;
 231: 
 232:     /** The default limit below which chart scaling kicks in. */
 233:     public static final int DEFAULT_MAXIMUM_DRAW_HEIGHT = 600;
 234: 
 235:     /** The minimum size required to perform a zoom on a rectangle */
 236:     public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10;
 237: 
 238:     /** Properties action command. */
 239:     public static final String PROPERTIES_COMMAND = "PROPERTIES";
 240: 
 241:     /** Save action command. */
 242:     public static final String SAVE_COMMAND = "SAVE";
 243: 
 244:     /** Print action command. */
 245:     public static final String PRINT_COMMAND = "PRINT";
 246: 
 247:     /** Zoom in (both axes) action command. */
 248:     public static final String ZOOM_IN_BOTH_COMMAND = "ZOOM_IN_BOTH";
 249: 
 250:     /** Zoom in (domain axis only) action command. */
 251:     public static final String ZOOM_IN_DOMAIN_COMMAND = "ZOOM_IN_DOMAIN";
 252: 
 253:     /** Zoom in (range axis only) action command. */
 254:     public static final String ZOOM_IN_RANGE_COMMAND = "ZOOM_IN_RANGE";
 255: 
 256:     /** Zoom out (both axes) action command. */
 257:     public static final String ZOOM_OUT_BOTH_COMMAND = "ZOOM_OUT_BOTH";
 258: 
 259:     /** Zoom out (domain axis only) action command. */
 260:     public static final String ZOOM_OUT_DOMAIN_COMMAND = "ZOOM_DOMAIN_BOTH";
 261: 
 262:     /** Zoom out (range axis only) action command. */
 263:     public static final String ZOOM_OUT_RANGE_COMMAND = "ZOOM_RANGE_BOTH";
 264: 
 265:     /** Zoom reset (both axes) action command. */
 266:     public static final String ZOOM_RESET_BOTH_COMMAND = "ZOOM_RESET_BOTH";
 267: 
 268:     /** Zoom reset (domain axis only) action command. */
 269:     public static final String ZOOM_RESET_DOMAIN_COMMAND = "ZOOM_RESET_DOMAIN";
 270: 
 271:     /** Zoom reset (range axis only) action command. */
 272:     public static final String ZOOM_RESET_RANGE_COMMAND = "ZOOM_RESET_RANGE";
 273: 
 274:     /** The chart that is displayed in the panel. */
 275:     private JFreeChart chart;
 276: 
 277:     /** Storage for registered (chart) mouse listeners. */
 278:     private transient EventListenerList chartMouseListeners;
 279: 
 280:     /** A flag that controls whether or not the off-screen buffer is used. */
 281:     private boolean useBuffer;
 282: 
 283:     /** A flag that indicates that the buffer should be refreshed. */
 284:     private boolean refreshBuffer;
 285: 
 286:     /** A buffer for the rendered chart. */
 287:     private transient Image chartBuffer;
 288: 
 289:     /** The height of the chart buffer. */
 290:     private int chartBufferHeight;
 291: 
 292:     /** The width of the chart buffer. */
 293:     private int chartBufferWidth;
 294: 
 295:     /**
 296:      * The minimum width for drawing a chart (uses scaling for smaller widths).
 297:      */
 298:     private int minimumDrawWidth;
 299: 
 300:     /**
 301:      * The minimum height for drawing a chart (uses scaling for smaller
 302:      * heights).
 303:      */
 304:     private int minimumDrawHeight;
 305: 
 306:     /**
 307:      * The maximum width for drawing a chart (uses scaling for bigger
 308:      * widths).
 309:      */
 310:     private int maximumDrawWidth;
 311: 
 312:     /**
 313:      * The maximum height for drawing a chart (uses scaling for bigger
 314:      * heights).
 315:      */
 316:     private int maximumDrawHeight;
 317: 
 318:     /** The popup menu for the frame. */
 319:     private JPopupMenu popup;
 320: 
 321:     /** The drawing info collected the last time the chart was drawn. */
 322:     private ChartRenderingInfo info;
 323: 
 324:     /** The chart anchor point. */
 325:     private Point2D anchor;
 326: 
 327:     /** The scale factor used to draw the chart. */
 328:     private double scaleX;
 329: 
 330:     /** The scale factor used to draw the chart. */
 331:     private double scaleY;
 332: 
 333:     /** The plot orientation. */
 334:     private PlotOrientation orientation = PlotOrientation.VERTICAL;
 335: 
 336:     /** A flag that controls whether or not domain zooming is enabled. */
 337:     private boolean domainZoomable = false;
 338: 
 339:     /** A flag that controls whether or not range zooming is enabled. */
 340:     private boolean rangeZoomable = false;
 341: 
 342:     /**
 343:      * The zoom rectangle starting point (selected by the user with a mouse
 344:      * click).  This is a point on the screen, not the chart (which may have
 345:      * been scaled up or down to fit the panel).
 346:      */
 347:     private Point2D zoomPoint = null;
 348: 
 349:     /** The zoom rectangle (selected by the user with the mouse). */
 350:     private transient Rectangle2D zoomRectangle = null;
 351: 
 352:     /** Controls if the zoom rectangle is drawn as an outline or filled. */
 353:     private boolean fillZoomRectangle = false;
 354: 
 355:     /** The minimum distance required to drag the mouse to trigger a zoom. */
 356:     private int zoomTriggerDistance;
 357: 
 358:     /** A flag that controls whether or not horizontal tracing is enabled. */
 359:     private boolean horizontalAxisTrace = false;
 360: 
 361:     /** A flag that controls whether or not vertical tracing is enabled. */
 362:     private boolean verticalAxisTrace = false;
 363: 
 364:     /** A vertical trace line. */
 365:     private transient Line2D verticalTraceLine;
 366: 
 367:     /** A horizontal trace line. */
 368:     private transient Line2D horizontalTraceLine;
 369: 
 370:     /** Menu item for zooming in on a chart (both axes). */
 371:     private JMenuItem zoomInBothMenuItem;
 372: 
 373:     /** Menu item for zooming in on a chart (domain axis). */
 374:     private JMenuItem zoomInDomainMenuItem;
 375: 
 376:     /** Menu item for zooming in on a chart (range axis). */
 377:     private JMenuItem zoomInRangeMenuItem;
 378: 
 379:     /** Menu item for zooming out on a chart. */
 380:     private JMenuItem zoomOutBothMenuItem;
 381: 
 382:     /** Menu item for zooming out on a chart (domain axis). */
 383:     private JMenuItem zoomOutDomainMenuItem;
 384: 
 385:     /** Menu item for zooming out on a chart (range axis). */
 386:     private JMenuItem zoomOutRangeMenuItem;
 387: 
 388:     /** Menu item for resetting the zoom (both axes). */
 389:     private JMenuItem zoomResetBothMenuItem;
 390: 
 391:     /** Menu item for resetting the zoom (domain axis only). */
 392:     private JMenuItem zoomResetDomainMenuItem;
 393: 
 394:     /** Menu item for resetting the zoom (range axis only). */
 395:     private JMenuItem zoomResetRangeMenuItem;
 396: 
 397:     /**
 398:      * The default directory for saving charts to file.
 399:      *
 400:      * @since 1.0.7
 401:      */
 402:     private File defaultDirectoryForSaveAs;
 403: 
 404:     /** A flag that controls whether or not file extensions are enforced. */
 405:     private boolean enforceFileExtensions;
 406: 
 407:     /** A flag that indicates if original tooltip delays are changed. */
 408:     private boolean ownToolTipDelaysActive;
 409: 
 410:     /** Original initial tooltip delay of ToolTipManager.sharedInstance(). */
 411:     private int originalToolTipInitialDelay;
 412: 
 413:     /** Original reshow tooltip delay of ToolTipManager.sharedInstance(). */
 414:     private int originalToolTipReshowDelay;
 415: 
 416:     /** Original dismiss tooltip delay of ToolTipManager.sharedInstance(). */
 417:     private int originalToolTipDismissDelay;
 418: 
 419:     /** Own initial tooltip delay to be used in this chart panel. */
 420:     private int ownToolTipInitialDelay;
 421: 
 422:     /** Own reshow tooltip delay to be used in this chart panel. */
 423:     private int ownToolTipReshowDelay;
 424: 
 425:     /** Own dismiss tooltip delay to be used in this chart panel. */
 426:     private int ownToolTipDismissDelay;
 427: 
 428:     /** The factor used to zoom in on an axis range. */
 429:     private double zoomInFactor = 0.5;
 430: 
 431:     /** The factor used to zoom out on an axis range. */
 432:     private double zoomOutFactor = 2.0;
 433: 
 434:     /**
 435:      * A flag that controls whether zoom operations are centred on the
 436:      * current anchor point, or the centre point of the relevant axis.
 437:      *
 438:      * @since 1.0.7
 439:      */
 440:     private boolean zoomAroundAnchor;
 441: 
 442:     /** The resourceBundle for the localization. */
 443:     protected static ResourceBundle localizationResources
 444:             = ResourceBundle.getBundle("org.jfree.chart.LocalizationBundle");
 445: 
 446:     /**
 447:      * Constructs a panel that displays the specified chart.
 448:      *
 449:      * @param chart  the chart.
 450:      */
 451:     public ChartPanel(JFreeChart chart) {
 452: 
 453:         this(
 454:             chart,
 455:             DEFAULT_WIDTH,
 456:             DEFAULT_HEIGHT,
 457:             DEFAULT_MINIMUM_DRAW_WIDTH,
 458:             DEFAULT_MINIMUM_DRAW_HEIGHT,
 459:             DEFAULT_MAXIMUM_DRAW_WIDTH,
 460:             DEFAULT_MAXIMUM_DRAW_HEIGHT,
 461:             DEFAULT_BUFFER_USED,
 462:             true,  // properties
 463:             true,  // save
 464:             true,  // print
 465:             true,  // zoom
 466:             true   // tooltips
 467:         );
 468: 
 469:     }
 470: 
 471:     /**
 472:      * Constructs a panel containing a chart.
 473:      *
 474:      * @param chart  the chart.
 475:      * @param useBuffer  a flag controlling whether or not an off-screen buffer
 476:      *                   is used.
 477:      */
 478:     public ChartPanel(JFreeChart chart, boolean useBuffer) {
 479: 
 480:         this(chart,
 481:              DEFAULT_WIDTH,
 482:              DEFAULT_HEIGHT,
 483:              DEFAULT_MINIMUM_DRAW_WIDTH,
 484:              DEFAULT_MINIMUM_DRAW_HEIGHT,
 485:              DEFAULT_MAXIMUM_DRAW_WIDTH,
 486:              DEFAULT_MAXIMUM_DRAW_HEIGHT,
 487:              useBuffer,
 488:              true,  // properties
 489:              true,  // save
 490:              true,  // print
 491:              true,  // zoom
 492:              true   // tooltips
 493:              );
 494: 
 495:     }
 496: 
 497:     /**
 498:      * Constructs a JFreeChart panel.
 499:      *
 500:      * @param chart  the chart.
 501:      * @param properties  a flag indicating whether or not the chart property
 502:      *                    editor should be available via the popup menu.
 503:      * @param save  a flag indicating whether or not save options should be
 504:      *              available via the popup menu.
 505:      * @param print  a flag indicating whether or not the print option
 506:      *               should be available via the popup menu.
 507:      * @param zoom  a flag indicating whether or not zoom options should
 508:      *              be added to the popup menu.
 509:      * @param tooltips  a flag indicating whether or not tooltips should be
 510:      *                  enabled for the chart.
 511:      */
 512:     public ChartPanel(JFreeChart chart,
 513:                       boolean properties,
 514:                       boolean save,
 515:                       boolean print,
 516:                       boolean zoom,
 517:                       boolean tooltips) {
 518: 
 519:         this(chart,
 520:              DEFAULT_WIDTH,
 521:              DEFAULT_HEIGHT,
 522:              DEFAULT_MINIMUM_DRAW_WIDTH,
 523:              DEFAULT_MINIMUM_DRAW_HEIGHT,
 524:              DEFAULT_MAXIMUM_DRAW_WIDTH,
 525:              DEFAULT_MAXIMUM_DRAW_HEIGHT,
 526:              DEFAULT_BUFFER_USED,
 527:              properties,
 528:              save,
 529:              print,
 530:              zoom,
 531:              tooltips
 532:              );
 533: 
 534:     }
 535: 
 536:     /**
 537:      * Constructs a JFreeChart panel.
 538:      *
 539:      * @param chart  the chart.
 540:      * @param width  the preferred width of the panel.
 541:      * @param height  the preferred height of the panel.
 542:      * @param minimumDrawWidth  the minimum drawing width.
 543:      * @param minimumDrawHeight  the minimum drawing height.
 544:      * @param maximumDrawWidth  the maximum drawing width.
 545:      * @param maximumDrawHeight  the maximum drawing height.
 546:      * @param useBuffer  a flag that indicates whether to use the off-screen
 547:      *                   buffer to improve performance (at the expense of
 548:      *                   memory).
 549:      * @param properties  a flag indicating whether or not the chart property
 550:      *                    editor should be available via the popup menu.
 551:      * @param save  a flag indicating whether or not save options should be
 552:      *              available via the popup menu.
 553:      * @param print  a flag indicating whether or not the print option
 554:      *               should be available via the popup menu.
 555:      * @param zoom  a flag indicating whether or not zoom options should be
 556:      *              added to the popup menu.
 557:      * @param tooltips  a flag indicating whether or not tooltips should be
 558:      *                  enabled for the chart.
 559:      */
 560:     public ChartPanel(JFreeChart chart,
 561:                       int width,
 562:                       int height,
 563:                       int minimumDrawWidth,
 564:                       int minimumDrawHeight,
 565:                       int maximumDrawWidth,
 566:                       int maximumDrawHeight,
 567:                       boolean useBuffer,
 568:                       boolean properties,
 569:                       boolean save,
 570:                       boolean print,
 571:                       boolean zoom,
 572:                       boolean tooltips) {
 573: 
 574:         this.setChart(chart);
 575:         this.chartMouseListeners = new EventListenerList();
 576:         this.info = new ChartRenderingInfo();
 577:         setPreferredSize(new Dimension(width, height));
 578:         this.useBuffer = useBuffer;
 579:         this.refreshBuffer = false;
 580:         this.minimumDrawWidth = minimumDrawWidth;
 581:         this.minimumDrawHeight = minimumDrawHeight;
 582:         this.maximumDrawWidth = maximumDrawWidth;
 583:         this.maximumDrawHeight = maximumDrawHeight;
 584:         this.zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE;
 585: 
 586:         // set up popup menu...
 587:         this.popup = null;
 588:         if (properties || save || print || zoom) {
 589:             this.popup = createPopupMenu(properties, save, print, zoom);
 590:         }
 591: 
 592:         enableEvents(AWTEvent.MOUSE_EVENT_MASK);
 593:         enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
 594:         setDisplayToolTips(tooltips);
 595:         addMouseListener(this);
 596:         addMouseMotionListener(this);
 597: 
 598:         this.defaultDirectoryForSaveAs = null;
 599:         this.enforceFileExtensions = true;
 600: 
 601:         // initialize ChartPanel-specific tool tip delays with
 602:         // values the from ToolTipManager.sharedInstance()
 603:         ToolTipManager ttm = ToolTipManager.sharedInstance();
 604:         this.ownToolTipInitialDelay = ttm.getInitialDelay();
 605:         this.ownToolTipDismissDelay = ttm.getDismissDelay();
 606:         this.ownToolTipReshowDelay = ttm.getReshowDelay();
 607: 
 608:         this.zoomAroundAnchor = false;
 609:     }
 610: 
 611:     /**
 612:      * Returns the chart contained in the panel.
 613:      *
 614:      * @return The chart (possibly <code>null</code>).
 615:      */
 616:     public JFreeChart getChart() {
 617:         return this.chart;
 618:     }
 619: 
 620:     /**
 621:      * Sets the chart that is displayed in the panel.
 622:      *
 623:      * @param chart  the chart (<code>null</code> permitted).
 624:      */
 625:     public void setChart(JFreeChart chart) {
 626: 
 627:         // stop listening for changes to the existing chart
 628:         if (this.chart != null) {
 629:             this.chart.removeChangeListener(this);
 630:             this.chart.removeProgressListener(this);
 631:         }
 632: 
 633:         // add the new chart
 634:         this.chart = chart;
 635:         if (chart != null) {
 636:             this.chart.addChangeListener(this);
 637:             this.chart.addProgressListener(this);
 638:             Plot plot = chart.getPlot();
 639:             this.domainZoomable = false;
 640:             this.rangeZoomable = false;
 641:             if (plot instanceof Zoomable) {
 642:                 Zoomable z = (Zoomable) plot;
 643:                 this.domainZoomable = z.isDomainZoomable();
 644:                 this.rangeZoomable = z.isRangeZoomable();
 645:                 this.orientation = z.getOrientation();
 646:             }
 647:         }
 648:         else {
 649:             this.domainZoomable = false;
 650:             this.rangeZoomable = false;
 651:         }
 652:         if (this.useBuffer) {
 653:             this.refreshBuffer = true;
 654:         }
 655:         repaint();
 656: 
 657:     }
 658: 
 659:     /**
 660:      * Returns the minimum drawing width for charts.
 661:      * <P>
 662:      * If the width available on the panel is less than this, then the chart is
 663:      * drawn at the minimum width then scaled down to fit.
 664:      *
 665:      * @return The minimum drawing width.
 666:      */
 667:     public int getMinimumDrawWidth() {
 668:         return this.minimumDrawWidth;
 669:     }
 670: 
 671:     /**
 672:      * Sets the minimum drawing width for the chart on this panel.
 673:      * <P>
 674:      * At the time the chart is drawn on the panel, if the available width is
 675:      * less than this amount, the chart will be drawn using the minimum width
 676:      * then scaled down to fit the available space.
 677:      *
 678:      * @param width  The width.
 679:      */
 680:     public void setMinimumDrawWidth(int width) {
 681:         this.minimumDrawWidth = width;
 682:     }
 683: 
 684:     /**
 685:      * Returns the maximum drawing width for charts.
 686:      * <P>
 687:      * If the width available on the panel is greater than this, then the chart
 688:      * is drawn at the maximum width then scaled up to fit.
 689:      *
 690:      * @return The maximum drawing width.
 691:      */
 692:     public int getMaximumDrawWidth() {
 693:         return this.maximumDrawWidth;
 694:     }
 695: 
 696:     /**
 697:      * Sets the maximum drawing width for the chart on this panel.
 698:      * <P>
 699:      * At the time the chart is drawn on the panel, if the available width is
 700:      * greater than this amount, the chart will be drawn using the maximum
 701:      * width then scaled up to fit the available space.
 702:      *
 703:      * @param width  The width.
 704:      */
 705:     public void setMaximumDrawWidth(int width) {
 706:         this.maximumDrawWidth = width;
 707:     }
 708: 
 709:     /**
 710:      * Returns the minimum drawing height for charts.
 711:      * <P>
 712:      * If the height available on the panel is less than this, then the chart
 713:      * is drawn at the minimum height then scaled down to fit.
 714:      *
 715:      * @return The minimum drawing height.
 716:      */
 717:     public int getMinimumDrawHeight() {
 718:         return this.minimumDrawHeight;
 719:     }
 720: 
 721:     /**
 722:      * Sets the minimum drawing height for the chart on this panel.
 723:      * <P>
 724:      * At the time the chart is drawn on the panel, if the available height is
 725:      * less than this amount, the chart will be drawn using the minimum height
 726:      * then scaled down to fit the available space.
 727:      *
 728:      * @param height  The height.
 729:      */
 730:     public void setMinimumDrawHeight(int height) {
 731:         this.minimumDrawHeight = height;
 732:     }
 733: 
 734:     /**
 735:      * Returns the maximum drawing height for charts.
 736:      * <P>
 737:      * If the height available on the panel is greater than this, then the
 738:      * chart is drawn at the maximum height then scaled up to fit.
 739:      *
 740:      * @return The maximum drawing height.
 741:      */
 742:     public int getMaximumDrawHeight() {
 743:         return this.maximumDrawHeight;
 744:     }
 745: 
 746:     /**
 747:      * Sets the maximum drawing height for the chart on this panel.
 748:      * <P>
 749:      * At the time the chart is drawn on the panel, if the available height is
 750:      * greater than this amount, the chart will be drawn using the maximum
 751:      * height then scaled up to fit the available space.
 752:      *
 753:      * @param height  The height.
 754:      */
 755:     public void setMaximumDrawHeight(int height) {
 756:         this.maximumDrawHeight = height;
 757:     }
 758: 
 759:     /**
 760:      * Returns the X scale factor for the chart.  This will be 1.0 if no
 761:      * scaling has been used.
 762:      *
 763:      * @return The scale factor.
 764:      */
 765:     public double getScaleX() {
 766:         return this.scaleX;
 767:     }
 768: 
 769:     /**
 770:      * Returns the Y scale factory for the chart.  This will be 1.0 if no
 771:      * scaling has been used.
 772:      *
 773:      * @return The scale factor.
 774:      */
 775:     public double getScaleY() {
 776:         return this.scaleY;
 777:     }
 778: 
 779:     /**
 780:      * Returns the anchor point.
 781:      *
 782:      * @return The anchor point (possibly <code>null</code>).
 783:      */
 784:     public Point2D getAnchor() {
 785:         return this.anchor;
 786:     }
 787: 
 788:     /**
 789:      * Sets the anchor point.  This method is provided for the use of
 790:      * subclasses, not end users.
 791:      *
 792:      * @param anchor  the anchor point (<code>null</code> permitted).
 793:      */
 794:     protected void setAnchor(Point2D anchor) {
 795:         this.anchor = anchor;
 796:     }
 797: 
 798:     /**
 799:      * Returns the popup menu.
 800:      *
 801:      * @return The popup menu.
 802:      */
 803:     public JPopupMenu getPopupMenu() {
 804:         return this.popup;
 805:     }
 806: 
 807:     /**
 808:      * Sets the popup menu for the panel.
 809:      *
 810:      * @param popup  the popup menu (<code>null</code> permitted).
 811:      */
 812:     public void setPopupMenu(JPopupMenu popup) {
 813:         this.popup = popup;
 814:     }
 815: 
 816:     /**
 817:      * Returns the chart rendering info from the most recent chart redraw.
 818:      *
 819:      * @return The chart rendering info.
 820:      */
 821:     public ChartRenderingInfo getChartRenderingInfo() {
 822:         return this.info;
 823:     }
 824: 
 825:     /**
 826:      * A convenience method that switches on mouse-based zooming.
 827:      *
 828:      * @param flag  <code>true</code> enables zooming and rectangle fill on
 829:      *              zoom.
 830:      */
 831:     public void setMouseZoomable(boolean flag) {
 832:         setMouseZoomable(flag, true);
 833:     }
 834: 
 835:     /**
 836:      * A convenience method that switches on mouse-based zooming.
 837:      *
 838:      * @param flag  <code>true</code> if zooming enabled
 839:      * @param fillRectangle  <code>true</code> if zoom rectangle is filled,
 840:      *                       false if rectangle is shown as outline only.
 841:      */
 842:     public void setMouseZoomable(boolean flag, boolean fillRectangle) {
 843:         setDomainZoomable(flag);
 844:         setRangeZoomable(flag);
 845:         setFillZoomRectangle(fillRectangle);
 846:     }
 847: 
 848:     /**
 849:      * Returns the flag that determines whether or not zooming is enabled for
 850:      * the domain axis.
 851:      *
 852:      * @return A boolean.
 853:      */
 854:     public boolean isDomainZoomable() {
 855:         return this.domainZoomable;
 856:     }
 857: 
 858:     /**
 859:      * Sets the flag that controls whether or not zooming is enable for the
 860:      * domain axis.  A check is made to ensure that the current plot supports
 861:      * zooming for the domain values.
 862:      *
 863:      * @param flag  <code>true</code> enables zooming if possible.
 864:      */
 865:     public void setDomainZoomable(boolean flag) {
 866:         if (flag) {
 867:             Plot plot = this.chart.getPlot();
 868:             if (plot instanceof Zoomable) {
 869:                 Zoomable z = (Zoomable) plot;
 870:                 this.domainZoomable = flag && (z.isDomainZoomable());
 871:             }
 872:         }
 873:         else {
 874:             this.domainZoomable = false;
 875:         }
 876:     }
 877: 
 878:     /**
 879:      * Returns the flag that determines whether or not zooming is enabled for
 880:      * the range axis.
 881:      *
 882:      * @return A boolean.
 883:      */
 884:     public boolean isRangeZoomable() {
 885:         return this.rangeZoomable;
 886:     }
 887: 
 888:     /**
 889:      * A flag that controls mouse-based zooming on the vertical axis.
 890:      *
 891:      * @param flag  <code>true</code> enables zooming.
 892:      */
 893:     public void setRangeZoomable(boolean flag) {
 894:         if (flag) {
 895:             Plot plot = this.chart.getPlot();
 896:             if (plot instanceof Zoomable) {
 897:                 Zoomable z = (Zoomable) plot;
 898:                 this.rangeZoomable = flag && (z.isRangeZoomable());
 899:             }
 900:         }
 901:         else {
 902:             this.rangeZoomable = false;
 903:         }
 904:     }
 905: 
 906:     /**
 907:      * Returns the flag that controls whether or not the zoom rectangle is
 908:      * filled when drawn.
 909:      *
 910:      * @return A boolean.
 911:      */
 912:     public boolean getFillZoomRectangle() {
 913:         return this.fillZoomRectangle;
 914:     }
 915: 
 916:     /**
 917:      * A flag that controls how the zoom rectangle is drawn.
 918:      *
 919:      * @param flag  <code>true</code> instructs to fill the rectangle on
 920:      *              zoom, otherwise it will be outlined.
 921:      */
 922:     public void setFillZoomRectangle(boolean flag) {
 923:         this.fillZoomRectangle = flag;
 924:     }
 925: 
 926:     /**
 927:      * Returns the zoom trigger distance.  This controls how far the mouse must
 928:      * move before a zoom action is triggered.
 929:      *
 930:      * @return The distance (in Java2D units).
 931:      */
 932:     public int getZoomTriggerDistance() {
 933:         return this.zoomTriggerDistance;
 934:     }
 935: 
 936:     /**
 937:      * Sets the zoom trigger distance.  This controls how far the mouse must
 938:      * move before a zoom action is triggered.
 939:      *
 940:      * @param distance  the distance (in Java2D units).
 941:      */
 942:     public void setZoomTriggerDistance(int distance) {
 943:         this.zoomTriggerDistance = distance;
 944:     }
 945: 
 946:     /**
 947:      * Returns the flag that controls whether or not a horizontal axis trace
 948:      * line is drawn over the plot area at the current mouse location.
 949:      *
 950:      * @return A boolean.
 951:      */
 952:     public boolean getHorizontalAxisTrace() {
 953:         return this.horizontalAxisTrace;
 954:     }
 955: 
 956:     /**
 957:      * A flag that controls trace lines on the horizontal axis.
 958:      *
 959:      * @param flag  <code>true</code> enables trace lines for the mouse
 960:      *      pointer on the horizontal axis.
 961:      */
 962:     public void setHorizontalAxisTrace(boolean flag) {
 963:         this.horizontalAxisTrace = flag;
 964:     }
 965: 
 966:     /**
 967:      * Returns the horizontal trace line.
 968:      *
 969:      * @return The horizontal trace line (possibly <code>null</code>).
 970:      */
 971:     protected Line2D getHorizontalTraceLine() {
 972:         return this.horizontalTraceLine;
 973:     }
 974: 
 975:     /**
 976:      * Sets the horizontal trace line.
 977:      *
 978:      * @param line  the line (<code>null</code> permitted).
 979:      */
 980:     protected void setHorizontalTraceLine(Line2D line) {
 981:         this.horizontalTraceLine = line;
 982:     }
 983: 
 984:     /**
 985:      * Returns the flag that controls whether or not a vertical axis trace
 986:      * line is drawn over the plot area at the current mouse location.
 987:      *
 988:      * @return A boolean.
 989:      */
 990:     public boolean getVerticalAxisTrace() {
 991:         return this.verticalAxisTrace;
 992:     }
 993: 
 994:     /**
 995:      * A flag that controls trace lines on the vertical axis.
 996:      *
 997:      * @param flag  <code>true</code> enables trace lines for the mouse
 998:      *              pointer on the vertical axis.
 999:      */
1000:     public void setVerticalAxisTrace(boolean flag) {
1001:         this.verticalAxisTrace = flag;
1002:     }
1003: 
1004:     /**
1005:      * Returns the vertical trace line.
1006:      *
1007:      * @return The vertical trace line (possibly <code>null</code>).
1008:      */
1009:     protected Line2D getVerticalTraceLine() {
1010:         return this.verticalTraceLine;
1011:     }
1012: 
1013:     /**
1014:      * Sets the vertical trace line.
1015:      *
1016:      * @param line  the line (<code>null</code> permitted).
1017:      */
1018:     protected void setVerticalTraceLine(Line2D line) {
1019:         this.verticalTraceLine = line;
1020:     }
1021: 
1022:     /**
1023:      * Returns the default directory for the "save as" option.
1024:      *
1025:      * @return The default directory (possibly <code>null</code>).
1026:      *
1027:      * @since 1.0.7
1028:      */
1029:     public File getDefaultDirectoryForSaveAs() {
1030:         return this.defaultDirectoryForSaveAs;
1031:     }
1032: 
1033:     /**
1034:      * Sets the default directory for the "save as" option.  If you set this
1035:      * to <code>null</code>, the user's default directory will be used.
1036:      *
1037:      * @param directory  the directory (<code>null</code> permitted).
1038:      *
1039:      * @since 1.0.7
1040:      */
1041:     public void setDefaultDirectoryForSaveAs(File directory) {
1042:         if (directory != null) {
1043:             if (!directory.isDirectory()) {
1044:                 throw new IllegalArgumentException(
1045:                         "The 'directory' argument is not a directory.");
1046:             }
1047:         }
1048:         this.defaultDirectoryForSaveAs = directory;
1049:     }
1050: 
1051:     /**
1052:      * Returns <code>true</code> if file extensions should be enforced, and
1053:      * <code>false</code> otherwise.
1054:      *
1055:      * @return The flag.
1056:      *
1057:      * @see #setEnforceFileExtensions(boolean)
1058:      */
1059:     public boolean isEnforceFileExtensions() {
1060:         return this.enforceFileExtensions;
1061:     }
1062: 
1063:     /**
1064:      * Sets a flag that controls whether or not file extensions are enforced.
1065:      *
1066:      * @param enforce  the new flag value.
1067:      *
1068:      * @see #isEnforceFileExtensions()
1069:      */
1070:     public void setEnforceFileExtensions(boolean enforce) {
1071:         this.enforceFileExtensions = enforce;
1072:     }
1073: 
1074:     /**
1075:      * Returns the flag that controls whether or not zoom operations are
1076:      * centered around the current anchor point.
1077:      *
1078:      * @return A boolean.
1079:      *
1080:      * @since 1.0.7
1081:      *
1082:      * @see #setZoomAroundAnchor(boolean)
1083:      */
1084:     public boolean getZoomAroundAnchor() {
1085:         return this.zoomAroundAnchor;
1086:     }
1087: 
1088:     /**
1089:      * Sets the flag that controls whether or not zoom operations are
1090:      * centered around the current anchor point.
1091:      *
1092:      * @param zoomAroundAnchor  the new flag value.
1093:      *
1094:      * @since 1.0.7
1095:      *
1096:      * @see #getZoomAroundAnchor()
1097:      */
1098:     public void setZoomAroundAnchor(boolean zoomAroundAnchor) {
1099:         this.zoomAroundAnchor = zoomAroundAnchor;
1100:     }
1101: 
1102:     /**
1103:      * Switches the display of tooltips for the panel on or off.  Note that
1104:      * tooltips can only be displayed if the chart has been configured to
1105:      * generate tooltip items.
1106:      *
1107:      * @param flag  <code>true</code> to enable tooltips, <code>false</code> to
1108:      *              disable tooltips.
1109:      */
1110:     public void setDisplayToolTips(boolean flag) {
1111:         if (flag) {
1112:             ToolTipManager.sharedInstance().registerComponent(this);
1113:         }
1114:         else {
1115:             ToolTipManager.sharedInstance().unregisterComponent(this);
1116:         }
1117:     }
1118: 
1119:     /**
1120:      * Returns a string for the tooltip.
1121:      *
1122:      * @param e  the mouse event.
1123:      *
1124:      * @return A tool tip or <code>null</code> if no tooltip is available.
1125:      */
1126:     public String getToolTipText(MouseEvent e) {
1127: 
1128:         String result = null;
1129:         if (this.info != null) {
1130:             EntityCollection entities = this.info.getEntityCollection();
1131:             if (entities != null) {
1132:                 Insets insets = getInsets();
1133:                 ChartEntity entity = entities.getEntity(
1134:                         (int) ((e.getX() - insets.left) / this.scaleX),
1135:                         (int) ((e.getY() - insets.top) / this.scaleY));
1136:                 if (entity != null) {
1137:                     result = entity.getToolTipText();
1138:                 }
1139:             }
1140:         }
1141:         return result;
1142: 
1143:     }
1144: 
1145:     /**
1146:      * Translates a Java2D point on the chart to a screen location.
1147:      *
1148:      * @param java2DPoint  the Java2D point.
1149:      *
1150:      * @return The screen location.
1151:      */
1152:     public Point translateJava2DToScreen(Point2D java2DPoint) {
1153:         Insets insets = getInsets();
1154:         int x = (int) (java2DPoint.getX() * this.scaleX + insets.left);
1155:         int y = (int) (java2DPoint.getY() * this.scaleY + insets.top);
1156:         return new Point(x, y);
1157:     }
1158: 
1159:     /**
1160:      * Translates a panel (component) location to a Java2D point.
1161:      *
1162:      * @param screenPoint  the screen location (<code>null</code> not
1163:      *                     permitted).
1164:      *
1165:      * @return The Java2D coordinates.
1166:      */
1167:     public Point2D translateScreenToJava2D(Point screenPoint) {
1168:         Insets insets = getInsets();
1169:         double x = (screenPoint.getX() - insets.left) / this.scaleX;
1170:         double y = (screenPoint.getY() - insets.top) / this.scaleY;
1171:         return new Point2D.Double(x, y);
1172:     }
1173: 
1174:     /**
1175:      * Applies any scaling that is in effect for the chart drawing to the
1176:      * given rectangle.
1177:      *
1178:      * @param rect  the rectangle (<code>null</code> not permitted).
1179:      *
1180:      * @return A new scaled rectangle.
1181:      */
1182:     public Rectangle2D scale(Rectangle2D rect) {
1183:         Insets insets = getInsets();
1184:         double x = rect.getX() * getScaleX() + insets.left;
1185:         double y = rect.getY() * getScaleY() + insets.top;
1186:         double w = rect.getWidth() * getScaleX();
1187:         double h = rect.getHeight() * getScaleY();
1188:         return new Rectangle2D.Double(x, y, w, h);
1189:     }
1190: 
1191:     /**
1192:      * Returns the chart entity at a given point.
1193:      * <P>
1194:      * This method will return null if there is (a) no entity at the given
1195:      * point, or (b) no entity collection has been generated.
1196:      *
1197:      * @param viewX  the x-coordinate.
1198:      * @param viewY  the y-coordinate.
1199:      *
1200:      * @return The chart entity (possibly <code>null</code>).
1201:      */
1202:     public ChartEntity getEntityForPoint(int viewX, int viewY) {
1203: 
1204:         ChartEntity result = null;
1205:         if (this.info != null) {
1206:             Insets insets = getInsets();
1207:             double x = (viewX - insets.left) / this.scaleX;
1208:             double y = (viewY - insets.top) / this.scaleY;
1209:             EntityCollection entities = this.info.getEntityCollection();
1210:             result = entities != null ? entities.getEntity(x, y) : null;
1211:         }
1212:         return result;
1213: 
1214:     }
1215: 
1216:     /**
1217:      * Returns the flag that controls whether or not the offscreen buffer
1218:      * needs to be refreshed.
1219:      *
1220:      * @return A boolean.
1221:      */
1222:     public boolean getRefreshBuffer() {
1223:         return this.refreshBuffer;
1224:     }
1225: 
1226:     /**
1227:      * Sets the refresh buffer flag.  This flag is used to avoid unnecessary
1228:      * redrawing of the chart when the offscreen image buffer is used.
1229:      *
1230:      * @param flag  <code>true</code> indicates that the buffer should be
1231:      *              refreshed.
1232:      */
1233:     public void setRefreshBuffer(boolean flag) {
1234:         this.refreshBuffer = flag;
1235:     }
1236: 
1237:     /**
1238:      * Paints the component by drawing the chart to fill the entire component,
1239:      * but allowing for the insets (which will be non-zero if a border has been
1240:      * set for this component).  To increase performance (at the expense of
1241:      * memory), an off-screen buffer image can be used.
1242:      *
1243:      * @param g  the graphics device for drawing on.
1244:      */
1245:     public void paintComponent(Graphics g) {
1246:         super.paintComponent(g);
1247:         if (this.chart == null) {
1248:             return;
1249:         }
1250:         Graphics2D g2 = (Graphics2D) g.create();
1251: 
1252:         // first determine the size of the chart rendering area...
1253:         Dimension size = getSize();
1254:         Insets insets = getInsets();
1255:         Rectangle2D available = new Rectangle2D.Double(insets.left, insets.top,
1256:                 size.getWidth() - insets.left - insets.right,
1257:                 size.getHeight() - insets.top - insets.bottom);
1258: 
1259:         // work out if scaling is required...
1260:         boolean scale = false;
1261:         double drawWidth = available.getWidth();
1262:         double drawHeight = available.getHeight();
1263:         this.scaleX = 1.0;
1264:         this.scaleY = 1.0;
1265: 
1266:         if (drawWidth < this.minimumDrawWidth) {
1267:             this.scaleX = drawWidth / this.minimumDrawWidth;
1268:             drawWidth = this.minimumDrawWidth;
1269:             scale = true;
1270:         }
1271:         else if (drawWidth > this.maximumDrawWidth) {
1272:             this.scaleX = drawWidth / this.maximumDrawWidth;
1273:             drawWidth = this.maximumDrawWidth;
1274:             scale = true;
1275:         }
1276: 
1277:         if (drawHeight < this.minimumDrawHeight) {
1278:             this.scaleY = drawHeight / this.minimumDrawHeight;
1279:             drawHeight = this.minimumDrawHeight;
1280:             scale = true;
1281:         }
1282:         else if (drawHeight > this.maximumDrawHeight) {
1283:             this.scaleY = drawHeight / this.maximumDrawHeight;
1284:             drawHeight = this.maximumDrawHeight;
1285:             scale = true;
1286:         }
1287: 
1288:         Rectangle2D chartArea = new Rectangle2D.Double(0.0, 0.0, drawWidth,
1289:                 drawHeight);
1290: 
1291:         // are we using the chart buffer?
1292:         if (this.useBuffer) {
1293: 
1294:             // if buffer is being refreshed, it needs clearing unless it is
1295:             // new - use the following flag to track this...
1296:             boolean clearBuffer = true;
1297: 
1298:             // do we need to resize the buffer?
1299:             if ((this.chartBuffer == null)
1300:                     || (this.chartBufferWidth != available.getWidth())
1301:                     || (this.chartBufferHeight != available.getHeight())) {
1302:                 this.chartBufferWidth = (int) available.getWidth();
1303:                 this.chartBufferHeight = (int) available.getHeight();
1304:                 this.chartBuffer = createImage(this.chartBufferWidth,
1305:                         this.chartBufferHeight);
1306: //                GraphicsConfiguration gc = g2.getDeviceConfiguration();
1307: //                this.chartBuffer = gc.createCompatibleImage(
1308: //                        this.chartBufferWidth, this.chartBufferHeight,
1309: //                        Transparency.TRANSLUCENT);
1310:                 this.refreshBuffer = true;
1311:                 clearBuffer = false;  // buffer is new, no clearing required
1312:             }
1313: 
1314:             // do we need to redraw the buffer?
1315:             if (this.refreshBuffer) {
1316: 
1317:                 this.refreshBuffer = false; // clear the flag
1318: 
1319:                 Rectangle2D bufferArea = new Rectangle2D.Double(
1320:                         0, 0, this.chartBufferWidth, this.chartBufferHeight);
1321: 
1322:                 Graphics2D bufferG2 = (Graphics2D)
1323:                         this.chartBuffer.getGraphics();
1324:                 if (clearBuffer) {
1325:                     bufferG2.clearRect(0, 0, this.chartBufferWidth,
1326:                             this.chartBufferHeight);
1327:                 }
1328:                 if (scale) {
1329:                     AffineTransform saved = bufferG2.getTransform();
1330:                     AffineTransform st = AffineTransform.getScaleInstance(
1331:                             this.scaleX, this.scaleY);
1332:                     bufferG2.transform(st);
1333:                     this.chart.draw(bufferG2, chartArea, this.anchor,
1334:                             this.info);
1335:                     bufferG2.setTransform(saved);
1336:                 }
1337:                 else {
1338:                     this.chart.draw(bufferG2, bufferArea, this.anchor,
1339:                             this.info);
1340:                 }
1341: 
1342:             }
1343: 
1344:             // zap the buffer onto the panel...
1345:             g2.drawImage(this.chartBuffer, insets.left, insets.top, this);
1346: 
1347:         }
1348: 
1349:         // or redrawing the chart every time...
1350:         else {
1351: 
1352:             AffineTransform saved = g2.getTransform();
1353:             g2.translate(insets.left, insets.top);
1354:             if (scale) {
1355:                 AffineTransform st = AffineTransform.getScaleInstance(
1356:                         this.scaleX, this.scaleY);
1357:                 g2.transform(st);
1358:             }
1359:             this.chart.draw(g2, chartArea, this.anchor, this.info);
1360:             g2.setTransform(saved);
1361: 
1362:         }
1363: 
1364:         // Redraw the zoom rectangle (if present)
1365:         drawZoomRectangle(g2);
1366: 
1367:         g2.dispose();
1368: 
1369:         this.anchor = null;
1370:         this.verticalTraceLine = null;
1371:         this.horizontalTraceLine = null;
1372: 
1373:     }
1374: 
1375:     /**
1376:      * Receives notification of changes to the chart, and redraws the chart.
1377:      *
1378:      * @param event  details of the chart change event.
1379:      */
1380:     public void chartChanged(ChartChangeEvent event) {
1381:         this.refreshBuffer = true;
1382:         Plot plot = this.chart.getPlot();
1383:         if (plot instanceof Zoomable) {
1384:             Zoomable z = (Zoomable) plot;
1385:             this.orientation = z.getOrientation();
1386:         }
1387:         repaint();
1388:     }
1389: 
1390:     /**
1391:      * Receives notification of a chart progress event.
1392:      *
1393:      * @param event  the event.
1394:      */
1395:     public void chartProgress(ChartProgressEvent event) {
1396:         // does nothing - override if necessary
1397:     }
1398: 
1399:     /**
1400:      * Handles action events generated by the popup menu.
1401:      *
1402:      * @param event  the event.
1403:      */
1404:     public void actionPerformed(ActionEvent event) {
1405: 
1406:         String command = event.getActionCommand();
1407: 
1408:         // many of the zoom methods need a screen location - all we have is
1409:         // the zoomPoint, but it might be null.  Here we grab the x and y
1410:         // coordinates, or use defaults...
1411:         double screenX = -1.0;
1412:         double screenY = -1.0;
1413:         if (this.zoomPoint != null) {
1414:             screenX = this.zoomPoint.getX();
1415:             screenY = this.zoomPoint.getY();
1416:         }
1417: 
1418:         if (command.equals(PROPERTIES_COMMAND)) {
1419:             doEditChartProperties();
1420:         }
1421:         else if (command.equals(SAVE_COMMAND)) {
1422:             try {
1423:                 doSaveAs();
1424:             }
1425:             catch (IOException e) {
1426:                 e.printStackTrace();
1427:             }
1428:         }
1429:         else if (command.equals(PRINT_COMMAND)) {
1430:             createChartPrintJob();
1431:         }
1432:         else if (command.equals(ZOOM_IN_BOTH_COMMAND)) {
1433:             zoomInBoth(screenX, screenY);
1434:         }
1435:         else if (command.equals(ZOOM_IN_DOMAIN_COMMAND)) {
1436:             zoomInDomain(screenX, screenY);
1437:         }
1438:         else if (command.equals(ZOOM_IN_RANGE_COMMAND)) {
1439:             zoomInRange(screenX, screenY);
1440:         }
1441:         else if (command.equals(ZOOM_OUT_BOTH_COMMAND)) {
1442:             zoomOutBoth(screenX, screenY);
1443:         }
1444:         else if (command.equals(ZOOM_OUT_DOMAIN_COMMAND)) {
1445:             zoomOutDomain(screenX, screenY);
1446:         }
1447:         else if (command.equals(ZOOM_OUT_RANGE_COMMAND)) {
1448:             zoomOutRange(screenX, screenY);
1449:         }
1450:         else if (command.equals(ZOOM_RESET_BOTH_COMMAND)) {
1451:             restoreAutoBounds();
1452:         }
1453:         else if (command.equals(ZOOM_RESET_DOMAIN_COMMAND)) {
1454:             restoreAutoDomainBounds();
1455:         }
1456:         else if (command.equals(ZOOM_RESET_RANGE_COMMAND)) {
1457:             restoreAutoRangeBounds();
1458:         }
1459: 
1460:     }
1461: 
1462:     /**
1463:      * Handles a 'mouse entered' event. This method changes the tooltip delays
1464:      * of ToolTipManager.sharedInstance() to the possibly different values set
1465:      * for this chart panel.
1466:      *
1467:      * @param e  the mouse event.
1468:      */
1469:     public void mouseEntered(MouseEvent e) {
1470:         if (!this.ownToolTipDelaysActive) {
1471:             ToolTipManager ttm = ToolTipManager.sharedInstance();
1472: 
1473:             this.originalToolTipInitialDelay = ttm.getInitialDelay();
1474:             ttm.setInitialDelay(this.ownToolTipInitialDelay);
1475: 
1476:             this.originalToolTipReshowDelay = ttm.getReshowDelay();
1477:             ttm.setReshowDelay(this.ownToolTipReshowDelay);
1478: 
1479:             this.originalToolTipDismissDelay = ttm.getDismissDelay();
1480:             ttm.setDismissDelay(this.ownToolTipDismissDelay);
1481: 
1482:             this.ownToolTipDelaysActive = true;
1483:         }
1484:     }
1485: 
1486:     /**
1487:      * Handles a 'mouse exited' event. This method resets the tooltip delays of
1488:      * ToolTipManager.sharedInstance() to their
1489:      * original values in effect before mouseEntered()
1490:      *
1491:      * @param e  the mouse event.
1492:      */
1493:     public void mouseExited(MouseEvent e) {
1494:         if (this.ownToolTipDelaysActive) {
1495:             // restore original tooltip dealys
1496:             ToolTipManager ttm = ToolTipManager.sharedInstance();
1497:             ttm.setInitialDelay(this.originalToolTipInitialDelay);
1498:             ttm.setReshowDelay(this.originalToolTipReshowDelay);
1499:             ttm.setDismissDelay(this.originalToolTipDismissDelay);
1500:             this.ownToolTipDelaysActive = false;
1501:         }
1502:     }
1503: 
1504:     /**
1505:      * Handles a 'mouse pressed' event.
1506:      * <P>
1507:      * This event is the popup trigger on Unix/Linux.  For Windows, the popup
1508:      * trigger is the 'mouse released' event.
1509:      *
1510:      * @param e  The mouse event.
1511:      */
1512:     public void mousePressed(MouseEvent e) {
1513:         if (this.zoomRectangle == null) {
1514:             Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY());
1515:             if (screenDataArea != null) {
1516:                 this.zoomPoint = getPointInRectangle(e.getX(), e.getY(),
1517:                         screenDataArea);
1518:             }
1519:             else {
1520:                 this.zoomPoint = null;
1521:             }
1522:             if (e.isPopupTrigger()) {
1523:                 if (this.popup != null) {
1524:                     displayPopupMenu(e.getX(), e.getY());
1525:                 }
1526:             }
1527:         }
1528:     }
1529: 
1530:     /**
1531:      * Returns a point based on (x, y) but constrained to be within the bounds
1532:      * of the given rectangle.  This method could be moved to JCommon.
1533:      *
1534:      * @param x  the x-coordinate.
1535:      * @param y  the y-coordinate.
1536:      * @param area  the rectangle (<code>null</code> not permitted).
1537:      *
1538:      * @return A point within the rectangle.
1539:      */
1540:     private Point2D getPointInRectangle(int x, int y, Rectangle2D area) {
1541:         double xx = Math.max(area.getMinX(), Math.min(x, area.getMaxX()));
1542:         double yy = Math.max(area.getMinY(), Math.min(y, area.getMaxY()));
1543:         return new Point2D.Double(xx, yy);
1544:     }
1545: 
1546:     /**
1547:      * Handles a 'mouse dragged' event.
1548:      *
1549:      * @param e  the mouse event.
1550:      */
1551:     public void mouseDragged(MouseEvent e) {
1552: 
1553:         // if the popup menu has already been triggered, then ignore dragging...
1554:         if (this.popup != null && this.popup.isShowing()) {
1555:             return;
1556:         }
1557:         // if no initial zoom point was set, ignore dragging...
1558:         if (this.zoomPoint == null) {
1559:             return;
1560:         }
1561:         Graphics2D g2 = (Graphics2D) getGraphics();
1562: 
1563:         // Erase the previous zoom rectangle (if any)...
1564:         drawZoomRectangle(g2);
1565: 
1566:         boolean hZoom = false;
1567:         boolean vZoom = false;
1568:         if (this.orientation == PlotOrientation.HORIZONTAL) {
1569:             hZoom = this.rangeZoomable;
1570:             vZoom = this.domainZoomable;
1571:         }
1572:         else {
1573:             hZoom = this.domainZoomable;
1574:             vZoom = this.rangeZoomable;
1575:         }
1576:         Rectangle2D scaledDataArea = getScreenDataArea(
1577:                 (int) this.zoomPoint.getX(), (int) this.zoomPoint.getY());
1578:         if (hZoom && vZoom) {
1579:             // selected rectangle shouldn't extend outside the data area...
1580:             double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
1581:             double ymax = Math.min(e.getY(), scaledDataArea.getMaxY());
1582:             this.zoomRectangle = new Rectangle2D.Double(
1583:                     this.zoomPoint.getX(), this.zoomPoint.getY(),
1584:                     xmax - this.zoomPoint.getX(), ymax - this.zoomPoint.getY());
1585:         }
1586:         else if (hZoom) {
1587:             double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
1588:             this.zoomRectangle = new Rectangle2D.Double(
1589:                     this.zoomPoint.getX(), scaledDataArea.getMinY(),
1590:                     xmax - this.zoomPoint.getX(), scaledDataArea.getHeight());
1591:         }
1592:         else if (vZoom) {
1593:             double ymax = Math.min(e.getY(), scaledDataArea.getMaxY());
1594:             this.zoomRectangle = new Rectangle2D.Double(
1595:                     scaledDataArea.getMinX(), this.zoomPoint.getY(),
1596:                     scaledDataArea.getWidth(), ymax - this.zoomPoint.getY());
1597:         }
1598: 
1599:         // Draw the new zoom rectangle...
1600:         drawZoomRectangle(g2);
1601: 
1602:         g2.dispose();
1603: 
1604:     }
1605: 
1606:     /**
1607:      * Handles a 'mouse released' event.  On Windows, we need to check if this
1608:      * is a popup trigger, but only if we haven't already been tracking a zoom
1609:      * rectangle.
1610:      *
1611:      * @param e  information about the event.
1612:      */
1613:     public void mouseReleased(MouseEvent e) {
1614: 
1615:         if (this.zoomRectangle != null) {
1616:             boolean hZoom = false;
1617:             boolean vZoom = false;
1618:             if (this.orientation == PlotOrientation.HORIZONTAL) {
1619:                 hZoom = this.rangeZoomable;
1620:                 vZoom = this.domainZoomable;
1621:             }
1622:             else {
1623:                 hZoom = this.domainZoomable;
1624:                 vZoom = this.rangeZoomable;
1625:             }
1626: 
1627:             boolean zoomTrigger1 = hZoom && Math.abs(e.getX()
1628:                 - this.zoomPoint.getX()) >= this.zoomTriggerDistance;
1629:             boolean zoomTrigger2 = vZoom && Math.abs(e.getY()
1630:                 - this.zoomPoint.getY()) >= this.zoomTriggerDistance;
1631:             if (zoomTrigger1 || zoomTrigger2) {
1632:                 if ((hZoom && (e.getX() < this.zoomPoint.getX()))
1633:                     || (vZoom && (e.getY() < this.zoomPoint.getY()))) {
1634:                     restoreAutoBounds();
1635:                 }
1636:                 else {
1637:                     double x, y, w, h;
1638:                     Rectangle2D screenDataArea = getScreenDataArea(
1639:                             (int) this.zoomPoint.getX(),
1640:                             (int) this.zoomPoint.getY());
1641:                     double maxX = screenDataArea.getMaxX();
1642:                     double maxY = screenDataArea.getMaxY();
1643:                     // for mouseReleased event, (horizontalZoom || verticalZoom)
1644:                     // will be true, so we can just test for either being false;
1645:                     // otherwise both are true
1646:                     if (!vZoom) {
1647:                         x = this.zoomPoint.getX();
1648:                         y = screenDataArea.getMinY();
1649:                         w = Math.min(this.zoomRectangle.getWidth(),
1650:                                 maxX - this.zoomPoint.getX());
1651:                         h = screenDataArea.getHeight();
1652:                     }
1653:                     else if (!hZoom) {
1654:                         x = screenDataArea.getMinX();
1655:                         y = this.zoomPoint.getY();
1656:                         w = screenDataArea.getWidth();
1657:                         h = Math.min(this.zoomRectangle.getHeight(),
1658:                                 maxY - this.zoomPoint.getY());
1659:                     }
1660:                     else {
1661:                         x = this.zoomPoint.getX();
1662:                         y = this.zoomPoint.getY();
1663:                         w = Math.min(this.zoomRectangle.getWidth(),
1664:                                 maxX - this.zoomPoint.getX());
1665:                         h = Math.min(this.zoomRectangle.getHeight(),
1666:                                 maxY - this.zoomPoint.getY());
1667:                     }
1668:                     Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w, h);
1669:                     zoom(zoomArea);
1670:                 }
1671:                 this.zoomPoint = null;
1672:                 this.zoomRectangle = null;
1673:             }
1674:             else {
1675:                 // Erase the zoom rectangle
1676:                 Graphics2D g2 = (Graphics2D) getGraphics();
1677:                 drawZoomRectangle(g2);
1678:                 g2.dispose();
1679:                 this.zoomPoint = null;
1680:                 this.zoomRectangle = null;
1681:             }
1682: 
1683:         }
1684: 
1685:         else if (e.isPopupTrigger()) {
1686:             if (this.popup != null) {
1687:                 displayPopupMenu(e.getX(), e.getY());
1688:             }
1689:         }
1690: 
1691:     }
1692: 
1693:     /**
1694:      * Receives notification of mouse clicks on the panel. These are
1695:      * translated and passed on to any registered {@link ChartMouseListener}s.
1696:      *
1697:      * @param event  Information about the mouse event.
1698:      */
1699:     public void mouseClicked(MouseEvent event) {
1700: 
1701:         Insets insets = getInsets();
1702:         int x = (int) ((event.getX() - insets.left) / this.scaleX);
1703:         int y = (int) ((event.getY() - insets.top) / this.scaleY);
1704: 
1705:         this.anchor = new Point2D.Double(x, y);
1706:         if (this.chart == null) {
1707:             return;
1708:         }
1709:         this.chart.setNotify(true);  // force a redraw
1710:         // new entity code...
1711:         Object[] listeners = this.chartMouseListeners.getListeners(
1712:                 ChartMouseListener.class);
1713:         if (listeners.length == 0) {
1714:             return;
1715:         }
1716: 
1717:         ChartEntity entity = null;
1718:         if (this.info != null) {
1719:             EntityCollection entities = this.info.getEntityCollection();
1720:             if (entities != null) {
1721:                 entity = entities.getEntity(x, y);
1722:             }
1723:         }
1724:         ChartMouseEvent chartEvent = new ChartMouseEvent(getChart(), event,
1725:                 entity);
1726:         for (int i = listeners.length - 1; i >= 0; i -= 1) {
1727:             ((ChartMouseListener) listeners[i]).chartMouseClicked(chartEvent);
1728:         }
1729: 
1730:     }
1731: 
1732:     /**
1733:      * Implementation of the MouseMotionListener's method.
1734:      *
1735:      * @param e  the event.
1736:      */
1737:     public void mouseMoved(MouseEvent e) {
1738:         Graphics2D g2 = (Graphics2D) getGraphics();
1739:         if (this.horizontalAxisTrace) {
1740:             drawHorizontalAxisTrace(g2, e.getX());
1741:         }
1742:         if (this.verticalAxisTrace) {
1743:             drawVerticalAxisTrace(g2, e.getY());
1744:         }
1745:         g2.dispose();
1746: 
1747:         Object[] listeners = this.chartMouseListeners.getListeners(
1748:                 ChartMouseListener.class);
1749:         if (listeners.length == 0) {
1750:             return;
1751:         }
1752:         Insets insets = getInsets();
1753:         int x = (int) ((e.getX() - insets.left) / this.scaleX);
1754:         int y = (int) ((e.getY() - insets.top) / this.scaleY);
1755: 
1756:         ChartEntity entity = null;
1757:         if (this.info != null) {
1758:             EntityCollection entities = this.info.getEntityCollection();
1759:             if (entities != null) {
1760:                 entity = entities.getEntity(x, y);
1761:             }
1762:         }
1763: 
1764:         // we can only generate events if the panel's chart is not null
1765:         // (see bug report 1556951)
1766:         if (this.chart != null) {
1767:             ChartMouseEvent event = new ChartMouseEvent(getChart(), e, entity);
1768:             for (int i = listeners.length - 1; i >= 0; i -= 1) {
1769:                 ((ChartMouseListener) listeners[i]).chartMouseMoved(event);
1770:             }
1771:         }
1772: 
1773:     }
1774: 
1775:     /**
1776:      * Zooms in on an anchor point (specified in screen coordinate space).
1777:      *
1778:      * @param x  the x value (in screen coordinates).
1779:      * @param y  the y value (in screen coordinates).
1780:      */
1781:     public void zoomInBoth(double x, double y) {
1782:         zoomInDomain(x, y);
1783:         zoomInRange(x, y);
1784:     }
1785: 
1786:     /**
1787:      * Decreases the length of the domain axis, centered about the given
1788:      * coordinate on the screen.  The length of the domain axis is reduced
1789:      * by the value of {@link #getZoomInFactor()}.
1790:      *
1791:      * @param x  the x coordinate (in screen coordinates).
1792:      * @param y  the y-coordinate (in screen coordinates).
1793:      */
1794:     public void zoomInDomain(double x, double y) {
1795:         Plot p = this.chart.getPlot();
1796:         if (p instanceof Zoomable) {
1797:             Zoomable plot = (Zoomable) p;
1798:             plot.zoomDomainAxes(this.zoomInFactor, this.info.getPlotInfo(),
1799:                     translateScreenToJava2D(new Point((int) x, (int) y)),
1800:                     this.zoomAroundAnchor);
1801:         }
1802:     }
1803: 
1804:     /**
1805:      * Decreases the length of the range axis, centered about the given
1806:      * coordinate on the screen.  The length of the range axis is reduced by
1807:      * the value of {@link #getZoomInFactor()}.
1808:      *
1809:      * @param x  the x-coordinate (in screen coordinates).
1810:      * @param y  the y coordinate (in screen coordinates).
1811:      */
1812:     public void zoomInRange(double x, double y) {
1813:         Plot p = this.chart.getPlot();
1814:         if (p instanceof Zoomable) {
1815:             Zoomable z = (Zoomable) p;
1816:             z.zoomRangeAxes(this.zoomInFactor, this.info.getPlotInfo(),
1817:                     translateScreenToJava2D(new Point((int) x, (int) y)),
1818:                     this.zoomAroundAnchor);
1819:         }
1820:     }
1821: 
1822:     /**
1823:      * Zooms out on an anchor point (specified in screen coordinate space).
1824:      *
1825:      * @param x  the x value (in screen coordinates).
1826:      * @param y  the y value (in screen coordinates).
1827:      */
1828:     public void zoomOutBoth(double x, double y) {
1829:         zoomOutDomain(x, y);
1830:         zoomOutRange(x, y);
1831:     }
1832: 
1833:     /**
1834:      * Increases the length of the domain axis, centered about the given
1835:      * coordinate on the screen.  The length of the domain axis is increased
1836:      * by the value of {@link #getZoomOutFactor()}.
1837:      *
1838:      * @param x  the x coordinate (in screen coordinates).
1839:      * @param y  the y-coordinate (in screen coordinates).
1840:      */
1841:     public void zoomOutDomain(double x, double y) {
1842:         Plot p = this.chart.getPlot();
1843:         if (p instanceof Zoomable) {
1844:             Zoomable z = (Zoomable) p;
1845:             z.zoomDomainAxes(this.zoomOutFactor, this.info.getPlotInfo(),
1846:                     translateScreenToJava2D(new Point((int) x, (int) y)),
1847:                     this.zoomAroundAnchor);
1848:         }
1849:     }
1850: 
1851:     /**
1852:      * Increases the length the range axis, centered about the given
1853:      * coordinate on the screen.  The length of the range axis is increased
1854:      * by the value of {@link #getZoomOutFactor()}.
1855:      *
1856:      * @param x  the x coordinate (in screen coordinates).
1857:      * @param y  the y-coordinate (in screen coordinates).
1858:      */
1859:     public void zoomOutRange(double x, double y) {
1860:         Plot p = this.chart.getPlot();
1861:         if (p instanceof Zoomable) {
1862:             Zoomable z = (Zoomable) p;
1863:             z.zoomRangeAxes(this.zoomOutFactor, this.info.getPlotInfo(),
1864:                     translateScreenToJava2D(new Point((int) x, (int) y)),
1865:                     this.zoomAroundAnchor);
1866:         }
1867:     }
1868: 
1869:     /**
1870:      * Zooms in on a selected region.
1871:      *
1872:      * @param selection  the selected region.
1873:      */
1874:     public void zoom(Rectangle2D selection) {
1875: 
1876:         // get the origin of the zoom selection in the Java2D space used for
1877:         // drawing the chart (that is, before any scaling to fit the panel)
1878:         Point2D selectOrigin = translateScreenToJava2D(new Point(
1879:                 (int) Math.ceil(selection.getX()),
1880:                 (int) Math.ceil(selection.getY())));
1881:         PlotRenderingInfo plotInfo = this.info.getPlotInfo();
1882:         Rectangle2D scaledDataArea = getScreenDataArea(
1883:                 (int) selection.getCenterX(), (int) selection.getCenterY());
1884:         if ((selection.getHeight() > 0) && (selection.getWidth() > 0)) {
1885: 
1886:             double hLower = (selection.getMinX() - scaledDataArea.getMinX())
1887:                 / scaledDataArea.getWidth();
1888:             double hUpper = (selection.getMaxX() - scaledDataArea.getMinX())
1889:                 / scaledDataArea.getWidth();
1890:             double vLower = (scaledDataArea.getMaxY() - selection.getMaxY())
1891:                 / scaledDataArea.getHeight();
1892:             double vUpper = (scaledDataArea.getMaxY() - selection.getMinY())
1893:                 / scaledDataArea.getHeight();
1894: 
1895:             Plot p = this.chart.getPlot();
1896:             if (p instanceof Zoomable) {
1897:                 Zoomable z = (Zoomable) p;
1898:                 if (z.getOrientation() == PlotOrientation.HORIZONTAL) {
1899:                     z.zoomDomainAxes(vLower, vUpper, plotInfo, selectOrigin);
1900:                     z.zoomRangeAxes(hLower, hUpper, plotInfo, selectOrigin);
1901:                 }
1902:                 else {
1903:                     z.zoomDomainAxes(hLower, hUpper, plotInfo, selectOrigin);
1904:                     z.zoomRangeAxes(vLower, vUpper, plotInfo, selectOrigin);
1905:                 }
1906:             }
1907: 
1908:         }
1909: 
1910:     }
1911: 
1912:     /**
1913:      * Restores the auto-range calculation on both axes.
1914:      */
1915:     public void restoreAutoBounds() {
1916:         restoreAutoDomainBounds();
1917:         restoreAutoRangeBounds();
1918:     }
1919: 
1920:     /**
1921:      * Restores the auto-range calculation on the domain axis.
1922:      */
1923:     public void restoreAutoDomainBounds() {
1924:         Plot p = this.chart.getPlot();
1925:         if (p instanceof Zoomable) {
1926:             Zoomable z = (Zoomable) p;
1927:             // we need to guard against this.zoomPoint being null
1928:             Point2D zp = (this.zoomPoint != null
1929:                     ? this.zoomPoint : new Point());
1930:             z.zoomDomainAxes(0.0, this.info.getPlotInfo(), zp);
1931:         }
1932:     }
1933: 
1934:     /**
1935:      * Restores the auto-range calculation on the range axis.
1936:      */
1937:     public void restoreAutoRangeBounds() {
1938:         Plot p = this.chart.getPlot();
1939:         if (p instanceof Zoomable) {
1940:             Zoomable z = (Zoomable) p;
1941:             // we need to guard against this.zoomPoint being null
1942:             Point2D zp = (this.zoomPoint != null
1943:                     ? this.zoomPoint : new Point());
1944:             z.zoomRangeAxes(0.0, this.info.getPlotInfo(), zp);
1945:         }
1946:     }
1947: 
1948:     /**
1949:      * Returns the data area for the chart (the area inside the axes) with the
1950:      * current scaling applied (that is, the area as it appears on screen).
1951:      *
1952:      * @return The scaled data area.
1953:      */
1954:     public Rectangle2D getScreenDataArea() {
1955:         Rectangle2D dataArea = this.info.getPlotInfo().getDataArea();
1956:         Insets insets = getInsets();
1957:         double x = dataArea.getX() * this.scaleX + insets.left;
1958:         double y = dataArea.getY() * this.scaleY + insets.top;
1959:         double w = dataArea.getWidth() * this.scaleX;
1960:         double h = dataArea.getHeight() * this.scaleY;
1961:         return new Rectangle2D.Double(x, y, w, h);
1962:     }
1963: 
1964:     /**
1965:      * Returns the data area (the area inside the axes) for the plot or subplot,
1966:      * with the current scaling applied.
1967:      *
1968:      * @param x  the x-coordinate (for subplot selection).
1969:      * @param y  the y-coordinate (for subplot selection).
1970:      *
1971:      * @return The scaled data area.
1972:      */
1973:     public Rectangle2D getScreenDataArea(int x, int y) {
1974:         PlotRenderingInfo plotInfo = this.info.getPlotInfo();
1975:         Rectangle2D result;
1976:         if (plotInfo.getSubplotCount() == 0) {
1977:             result = getScreenDataArea();
1978:         }
1979:         else {
1980:             // get the origin of the zoom selection in the Java2D space used for
1981:             // drawing the chart (that is, before any scaling to fit the panel)
1982:             Point2D selectOrigin = translateScreenToJava2D(new Point(x, y));
1983:             int subplotIndex = plotInfo.getSubplotIndex(selectOrigin);
1984:             if (subplotIndex == -1) {
1985:                 return null;
1986:             }
1987:             result = scale(plotInfo.getSubplotInfo(subplotIndex).getDataArea());
1988:         }
1989:         return result;
1990:     }
1991: 
1992:     /**
1993:      * Returns the initial tooltip delay value used inside this chart panel.
1994:      *
1995:      * @return An integer representing the initial delay value, in milliseconds.
1996:      *
1997:      * @see javax.swing.ToolTipManager#getInitialDelay()
1998:      */
1999:     public int getInitialDelay() {
2000:         return this.ownToolTipInitialDelay;
2001:     }
2002: 
2003:     /**
2004:      * Returns the reshow tooltip delay value used inside this chart panel.
2005:      *
2006:      * @return An integer representing the reshow  delay value, in milliseconds.
2007:      *
2008:      * @see javax.swing.ToolTipManager#getReshowDelay()
2009:      */
2010:     public int getReshowDelay() {
2011:         return this.ownToolTipReshowDelay;
2012:     }
2013: 
2014:     /**
2015:      * Returns the dismissal tooltip delay value used inside this chart panel.
2016:      *
2017:      * @return An integer representing the dismissal delay value, in
2018:      *         milliseconds.
2019:      *
2020:      * @see javax.swing.ToolTipManager#getDismissDelay()
2021:      */
2022:     public int getDismissDelay() {
2023:         return this.ownToolTipDismissDelay;
2024:     }
2025: 
2026:     /**
2027:      * Specifies the initial delay value for this chart panel.
2028:      *
2029:      * @param delay  the number of milliseconds to delay (after the cursor has
2030:      *               paused) before displaying.
2031:      *
2032:      * @see javax.swing.ToolTipManager#setInitialDelay(int)
2033:      */
2034:     public void setInitialDelay(int delay) {
2035:         this.ownToolTipInitialDelay = delay;
2036:     }
2037: 
2038:     /**
2039:      * Specifies the amount of time before the user has to wait initialDelay
2040:      * milliseconds before a tooltip will be shown.
2041:      *
2042:      * @param delay  time in milliseconds
2043:      *
2044:      * @see javax.swing.ToolTipManager#setReshowDelay(int)
2045:      */
2046:     public void setReshowDelay(int delay) {
2047:         this.ownToolTipReshowDelay = delay;
2048:     }
2049: 
2050:     /**
2051:      * Specifies the dismissal delay value for this chart panel.
2052:      *
2053:      * @param delay the number of milliseconds to delay before taking away the
2054:      *              tooltip
2055:      *
2056:      * @see javax.swing.ToolTipManager#setDismissDelay(int)
2057:      */
2058:     public void setDismissDelay(int delay) {
2059:         this.ownToolTipDismissDelay = delay;
2060:     }
2061: 
2062:     /**
2063:      * Returns the zoom in factor.
2064:      *
2065:      * @return The zoom in factor.
2066:      *
2067:      * @see #setZoomInFactor(double)
2068:      */
2069:     public double getZoomInFactor() {
2070:         return this.zoomInFactor;
2071:     }
2072: 
2073:     /**
2074:      * Sets the zoom in factor.
2075:      *
2076:      * @param factor  the factor.
2077:      *
2078:      * @see #getZoomInFactor()
2079:      */
2080:     public void setZoomInFactor(double factor) {
2081:         this.zoomInFactor = factor;
2082:     }
2083: 
2084:     /**
2085:      * Returns the zoom out factor.
2086:      *
2087:      * @return The zoom out factor.
2088:      *
2089:      * @see #setZoomOutFactor(double)
2090:      */
2091:     public double getZoomOutFactor() {
2092:         return this.zoomOutFactor;
2093:     }
2094: 
2095:     /**
2096:      * Sets the zoom out factor.
2097:      *
2098:      * @param factor  the factor.
2099:      *
2100:      * @see #getZoomOutFactor()
2101:      */
2102:     public void setZoomOutFactor(double factor) {
2103:         this.zoomOutFactor = factor;
2104:     }
2105: 
2106:     /**
2107:      * Draws zoom rectangle (if present).
2108:      * The drawing is performed in XOR mode, therefore
2109:      * when this method is called twice in a row,
2110:      * the second call will completely restore the state
2111:      * of the canvas.
2112:      *
2113:      * @param g2 the graphics device.
2114:      */
2115:     private void drawZoomRectangle(Graphics2D g2) {
2116:         // Set XOR mode to draw the zoom rectangle
2117:         g2.setXORMode(Color.gray);
2118:         if (this.zoomRectangle != null) {
2119:             if (this.fillZoomRectangle) {
2120:                 g2.fill(this.zoomRectangle);
2121:             }
2122:             else {
2123:                 g2.draw(this.zoomRectangle);
2124:             }
2125:         }
2126:         // Reset to the default 'overwrite' mode
2127:         g2.setPaintMode();
2128:     }
2129: 
2130:     /**
2131:      * Draws a vertical line used to trace the mouse position to the horizontal
2132:      * axis.
2133:      *
2134:      * @param g2 the graphics device.
2135:      * @param x  the x-coordinate of the trace line.
2136:      */
2137:     private void drawHorizontalAxisTrace(Graphics2D g2, int x) {
2138: 
2139:         Rectangle2D dataArea = getScreenDataArea();
2140: 
2141:         g2.setXORMode(Color.orange);
2142:         if (((int) dataArea.getMinX() < x) && (x < (int) dataArea.getMaxX())) {
2143: 
2144:             if (this.verticalTraceLine != null) {
2145:                 g2.draw(this.verticalTraceLine);
2146:                 this.verticalTraceLine.setLine(x, (int) dataArea.getMinY(), x,
2147:                         (int) dataArea.getMaxY());
2148:             }
2149:             else {
2150:                 this.verticalTraceLine = new Line2D.Float(x,
2151:                         (int) dataArea.getMinY(), x, (int) dataArea.getMaxY());
2152:             }
2153:             g2.draw(this.verticalTraceLine);
2154:         }
2155: 
2156:         // Reset to the default 'overwrite' mode
2157:         g2.setPaintMode();
2158:     }
2159: 
2160:     /**
2161:      * Draws a horizontal line used to trace the mouse position to the vertical
2162:      * axis.
2163:      *
2164:      * @param g2 the graphics device.
2165:      * @param y  the y-coordinate of the trace line.
2166:      */
2167:     private void drawVerticalAxisTrace(Graphics2D g2, int y) {
2168: 
2169:         Rectangle2D dataArea = getScreenDataArea();
2170: 
2171:         g2.setXORMode(Color.orange);
2172:         if (((int) dataArea.getMinY() < y) && (y < (int) dataArea.getMaxY())) {
2173: 
2174:             if (this.horizontalTraceLine != null) {
2175:                 g2.draw(this.horizontalTraceLine);
2176:                 this.horizontalTraceLine.setLine((int) dataArea.getMinX(), y,
2177:                         (int) dataArea.getMaxX(), y);
2178:             }
2179:             else {
2180:                 this.horizontalTraceLine = new Line2D.Float(
2181:                         (int) dataArea.getMinX(), y, (int) dataArea.getMaxX(),
2182:                         y);
2183:             }
2184:             g2.draw(this.horizontalTraceLine);
2185:         }
2186: 
2187:         // Reset to the default 'overwrite' mode
2188:         g2.setPaintMode();
2189:     }
2190: 
2191:     /**
2192:      * Displays a dialog that allows the user to edit the properties for the
2193:      * current chart.
2194:      *
2195:      * @since 1.0.3
2196:      */
2197:     public void doEditChartProperties() {
2198: 
2199:         ChartEditor editor = ChartEditorManager.getChartEditor(this.chart);
2200:         int result = JOptionPane.showConfirmDialog(this, editor,
2201:                 localizationResources.getString("Chart_Properties"),
2202:                 JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
2203:         if (result == JOptionPane.OK_OPTION) {
2204:             editor.updateChart(this.chart);
2205:         }
2206: 
2207:     }
2208: 
2209:     /**
2210:      * Opens a file chooser and gives the user an opportunity to save the chart
2211:      * in PNG format.
2212:      *
2213:      * @throws IOException if there is an I/O error.
2214:      */
2215:     public void doSaveAs() throws IOException {
2216: 
2217:         JFileChooser fileChooser = new JFileChooser();
2218:         fileChooser.setCurrentDirectory(this.defaultDirectoryForSaveAs);
2219:         ExtensionFileFilter filter = new ExtensionFileFilter(
2220:                 localizationResources.getString("PNG_Image_Files"), ".png");
2221:         fileChooser.addChoosableFileFilter(filter);
2222: 
2223:         int option = fileChooser.showSaveDialog(this);
2224:         if (option == JFileChooser.APPROVE_OPTION) {
2225:             String filename = fileChooser.getSelectedFile().getPath();
2226:             if (isEnforceFileExtensions()) {
2227:                 if (!filename.endsWith(".png")) {
2228:                     filename = filename + ".png";
2229:                 }
2230:             }
2231:             ChartUtilities.saveChartAsPNG(new File(filename), this.chart,
2232:                     getWidth(), getHeight());
2233:         }
2234: 
2235:     }
2236: 
2237:     /**
2238:      * Creates a print job for the chart.
2239:      */
2240:     public void createChartPrintJob() {
2241: 
2242:         PrinterJob job = PrinterJob.getPrinterJob();
2243:         PageFormat pf = job.defaultPage();
2244:         PageFormat pf2 = job.pageDialog(pf);
2245:         if (pf2 != pf) {
2246:             job.setPrintable(this, pf2);
2247:             if (job.printDialog()) {
2248:                 try {
2249:                     job.print();
2250:                 }
2251:                 catch (PrinterException e) {
2252:                     JOptionPane.showMessageDialog(this, e);
2253:                 }
2254:             }
2255:         }
2256: 
2257:     }
2258: 
2259:     /**
2260:      * Prints the chart on a single page.
2261:      *
2262:      * @param g  the graphics context.
2263:      * @param pf  the page format to use.
2264:      * @param pageIndex  the index of the page. If not <code>0</code>, nothing
2265:      *                   gets print.
2266:      *
2267:      * @return The result of printing.
2268:      */
2269:     public int print(Graphics g, PageFormat pf, int pageIndex) {
2270: 
2271:         if (pageIndex != 0) {
2272:             return NO_SUCH_PAGE;
2273:         }
2274:         Graphics2D g2 = (Graphics2D) g;
2275:         double x = pf.getImageableX();
2276:         double y = pf.getImageableY();
2277:         double w = pf.getImageableWidth();
2278:         double h = pf.getImageableHeight();
2279:         this.chart.draw(g2, new Rectangle2D.Double(x, y, w, h), this.anchor,
2280:                 null);
2281:         return PAGE_EXISTS;
2282: 
2283:     }
2284: 
2285:     /**
2286:      * Adds a listener to the list of objects listening for chart mouse events.
2287:      *
2288:      * @param listener  the listener (<code>null</code> not permitted).
2289:      */
2290:     public void addChartMouseListener(ChartMouseListener listener) {
2291:         if (listener == null) {
2292:             throw new IllegalArgumentException("Null 'listener' argument.");
2293:         }
2294:         this.chartMouseListeners.add(ChartMouseListener.class, listener);
2295:     }
2296: 
2297:     /**
2298:      * Removes a listener from the list of objects listening for chart mouse
2299:      * events.
2300:      *
2301:      * @param listener  the listener.
2302:      */
2303:     public void removeChartMouseListener(ChartMouseListener listener) {
2304:         this.chartMouseListeners.remove(ChartMouseListener.class, listener);
2305:     }
2306: 
2307:     /**
2308:      * Returns an array of the listeners of the given type registered with the
2309:      * panel.
2310:      *
2311:      * @param listenerType  the listener type.
2312:      *
2313:      * @return An array of listeners.
2314:      */
2315:     public EventListener[] getListeners(Class listenerType) {
2316:         if (listenerType == ChartMouseListener.class) {
2317:             // fetch listeners from local storage
2318:             return this.chartMouseListeners.getListeners(listenerType);
2319:         }
2320:         else {
2321:             return super.getListeners(listenerType);
2322:         }
2323:     }
2324: 
2325:     /**
2326:      * Creates a popup menu for the panel.
2327:      *
2328:      * @param properties  include a menu item for the chart property editor.
2329:      * @param save  include a menu item for saving the chart.
2330:      * @param print  include a menu item for printing the chart.
2331:      * @param zoom  include menu items for zooming.
2332:      *
2333:      * @return The popup menu.
2334:      */
2335:     protected JPopupMenu createPopupMenu(boolean properties,
2336:                                          boolean save,
2337:                                          boolean print,
2338:                                          boolean zoom) {
2339: 
2340:         JPopupMenu result = new JPopupMenu("Chart:");
2341:         boolean separator = false;
2342: 
2343:         if (properties) {
2344:             JMenuItem propertiesItem = new JMenuItem(
2345:                     localizationResources.getString("Properties..."));
2346:             propertiesItem.setActionCommand(PROPERTIES_COMMAND);
2347:             propertiesItem.addActionListener(this);
2348:             result.add(propertiesItem);
2349:             separator = true;
2350:         }
2351: 
2352:         if (save) {
2353:             if (separator) {
2354:                 result.addSeparator();
2355:                 separator = false;
2356:             }
2357:             JMenuItem saveItem = new JMenuItem(
2358:                     localizationResources.getString("Save_as..."));
2359:             saveItem.setActionCommand(SAVE_COMMAND);
2360:             saveItem.addActionListener(this);
2361:             result.add(saveItem);
2362:             separator = true;
2363:         }
2364: 
2365:         if (print) {
2366:             if (separator) {
2367:                 result.addSeparator();
2368:                 separator = false;
2369:             }
2370:             JMenuItem printItem = new JMenuItem(
2371:                     localizationResources.getString("Print..."));
2372:             printItem.setActionCommand(PRINT_COMMAND);
2373:             printItem.addActionListener(this);
2374:             result.add(printItem);
2375:             separator = true;
2376:         }
2377: 
2378:         if (zoom) {
2379:             if (separator) {
2380:                 result.addSeparator();
2381:                 separator = false;
2382:             }
2383: 
2384:             JMenu zoomInMenu = new JMenu(
2385:                     localizationResources.getString("Zoom_In"));
2386: 
2387:             this.zoomInBothMenuItem = new JMenuItem(
2388:                     localizationResources.getString("All_Axes"));
2389:             this.zoomInBothMenuItem.setActionCommand(ZOOM_IN_BOTH_COMMAND);
2390:             this.zoomInBothMenuItem.addActionListener(this);
2391:             zoomInMenu.add(this.zoomInBothMenuItem);
2392: 
2393:             zoomInMenu.addSeparator();
2394: 
2395:             this.zoomInDomainMenuItem = new JMenuItem(
2396:                     localizationResources.getString("Domain_Axis"));
2397:             this.zoomInDomainMenuItem.setActionCommand(ZOOM_IN_DOMAIN_COMMAND);
2398:             this.zoomInDomainMenuItem.addActionListener(this);
2399:             zoomInMenu.add(this.zoomInDomainMenuItem);
2400: 
2401:             this.zoomInRangeMenuItem = new JMenuItem(
2402:                     localizationResources.getString("Range_Axis"));
2403:             this.zoomInRangeMenuItem.setActionCommand(ZOOM_IN_RANGE_COMMAND);
2404:             this.zoomInRangeMenuItem.addActionListener(this);
2405:             zoomInMenu.add(this.zoomInRangeMenuItem);
2406: 
2407:             result.add(zoomInMenu);
2408: 
2409:             JMenu zoomOutMenu = new JMenu(
2410:                     localizationResources.getString("Zoom_Out"));
2411: 
2412:             this.zoomOutBothMenuItem = new JMenuItem(
2413:                     localizationResources.getString("All_Axes"));
2414:             this.zoomOutBothMenuItem.setActionCommand(ZOOM_OUT_BOTH_COMMAND);
2415:             this.zoomOutBothMenuItem.addActionListener(this);
2416:             zoomOutMenu.add(this.zoomOutBothMenuItem);
2417: 
2418:             zoomOutMenu.addSeparator();
2419: 
2420:             this.zoomOutDomainMenuItem = new JMenuItem(
2421:                     localizationResources.getString("Domain_Axis"));
2422:             this.zoomOutDomainMenuItem.setActionCommand(
2423:                     ZOOM_OUT_DOMAIN_COMMAND);
2424:             this.zoomOutDomainMenuItem.addActionListener(this);
2425:             zoomOutMenu.add(this.zoomOutDomainMenuItem);
2426: 
2427:             this.zoomOutRangeMenuItem = new JMenuItem(
2428:                     localizationResources.getString("Range_Axis"));
2429:             this.zoomOutRangeMenuItem.setActionCommand(ZOOM_OUT_RANGE_COMMAND);
2430:             this.zoomOutRangeMenuItem.addActionListener(this);
2431:             zoomOutMenu.add(this.zoomOutRangeMenuItem);
2432: 
2433:             result.add(zoomOutMenu);
2434: 
2435:             JMenu autoRangeMenu = new JMenu(
2436:                     localizationResources.getString("Auto_Range"));
2437: 
2438:             this.zoomResetBothMenuItem = new JMenuItem(
2439:                     localizationResources.getString("All_Axes"));
2440:             this.zoomResetBothMenuItem.setActionCommand(
2441:                     ZOOM_RESET_BOTH_COMMAND);
2442:             this.zoomResetBothMenuItem.addActionListener(this);
2443:             autoRangeMenu.add(this.zoomResetBothMenuItem);
2444: 
2445:             autoRangeMenu.addSeparator();
2446:             this.zoomResetDomainMenuItem = new JMenuItem(
2447:                     localizationResources.getString("Domain_Axis"));
2448:             this.zoomResetDomainMenuItem.setActionCommand(
2449:                     ZOOM_RESET_DOMAIN_COMMAND);
2450:             this.zoomResetDomainMenuItem.addActionListener(this);
2451:             autoRangeMenu.add(this.zoomResetDomainMenuItem);
2452: 
2453:             this.zoomResetRangeMenuItem = new JMenuItem(
2454:                     localizationResources.getString("Range_Axis"));
2455:             this.zoomResetRangeMenuItem.setActionCommand(
2456:                     ZOOM_RESET_RANGE_COMMAND);
2457:             this.zoomResetRangeMenuItem.addActionListener(this);
2458:             autoRangeMenu.add(this.zoomResetRangeMenuItem);
2459: 
2460:             result.addSeparator();
2461:             result.add(autoRangeMenu);
2462: 
2463:         }
2464: 
2465:         return result;
2466: 
2467:     }
2468: 
2469:     /**
2470:      * The idea is to modify the zooming options depending on the type of chart
2471:      * being displayed by the panel.
2472:      *
2473:      * @param x  horizontal position of the popup.
2474:      * @param y  vertical position of the popup.
2475:      */
2476:     protected void displayPopupMenu(int x, int y) {
2477: 
2478:         if (this.popup != null) {
2479: 
2480:             // go through each zoom menu item and decide whether or not to
2481:             // enable it...
2482:             Plot plot = this.chart.getPlot();
2483:             boolean isDomainZoomable = false;
2484:             boolean isRangeZoomable = false;
2485:             if (plot instanceof Zoomable) {
2486:                 Zoomable z = (Zoomable) plot;
2487:                 isDomainZoomable = z.isDomainZoomable();
2488:                 isRangeZoomable = z.isRangeZoomable();
2489:             }
2490: 
2491:             if (this.zoomInDomainMenuItem != null) {
2492:                 this.zoomInDomainMenuItem.setEnabled(isDomainZoomable);
2493:             }
2494:             if (this.zoomOutDomainMenuItem != null) {
2495:                 this.zoomOutDomainMenuItem.setEnabled(isDomainZoomable);
2496:             }
2497:             if (this.zoomResetDomainMenuItem != null) {
2498:                 this.zoomResetDomainMenuItem.setEnabled(isDomainZoomable);
2499:             }
2500: 
2501:             if (this.zoomInRangeMenuItem != null) {
2502:                 this.zoomInRangeMenuItem.setEnabled(isRangeZoomable);
2503:             }
2504:             if (this.zoomOutRangeMenuItem != null) {
2505:                 this.zoomOutRangeMenuItem.setEnabled(isRangeZoomable);
2506:             }
2507: 
2508:             if (this.zoomResetRangeMenuItem != null) {
2509:                 this.zoomResetRangeMenuItem.setEnabled(isRangeZoomable);
2510:             }
2511: 
2512:             if (this.zoomInBothMenuItem != null) {
2513:                 this.zoomInBothMenuItem.setEnabled(isDomainZoomable
2514:                         && isRangeZoomable);
2515:             }
2516:             if (this.zoomOutBothMenuItem != null) {
2517:                 this.zoomOutBothMenuItem.setEnabled(isDomainZoomable
2518:                         && isRangeZoomable);
2519:             }
2520:             if (this.zoomResetBothMenuItem != null) {
2521:                 this.zoomResetBothMenuItem.setEnabled(isDomainZoomable
2522:                         && isRangeZoomable);
2523:             }
2524: 
2525:             this.popup.show(this, x, y);
2526:         }
2527: 
2528:     }
2529: 
2530:     /* (non-Javadoc)
2531:      * @see javax.swing.JPanel#updateUI()
2532:      */
2533:     public void updateUI() {
2534:         // here we need to update the UI for the popup menu, if the panel
2535:         // has one...
2536:         if (this.popup != null) {
2537:             SwingUtilities.updateComponentTreeUI(this.popup);
2538:         }
2539:         super.updateUI();
2540:     }
2541: 
2542:     /**
2543:      * Provides serialization support.
2544:      *
2545:      * @param stream  the output stream.
2546:      *
2547:      * @throws IOException  if there is an I/O error.
2548:      */
2549:     private void writeObject(ObjectOutputStream stream) throws IOException {
2550:         stream.defaultWriteObject();
2551:     }
2552: 
2553:     /**
2554:      * Provides serialization support.
2555:      *
2556:      * @param stream  the input stream.
2557:      *
2558:      * @throws IOException  if there is an I/O error.
2559:      * @throws ClassNotFoundException  if there is a classpath problem.
2560:      */
2561:     private void readObject(ObjectInputStream stream)
2562:         throws IOException, ClassNotFoundException {
2563:         stream.defaultReadObject();
2564: 
2565:         // we create a new but empty chartMouseListeners list
2566:         this.chartMouseListeners = new EventListenerList();
2567: 
2568:         // register as a listener with sub-components...
2569:         if (this.chart != null) {
2570:             this.chart.addChangeListener(this);
2571:         }
2572: 
2573:     }
2574: 
2575: 
2576: }