Source for org.jfree.chart.ChartPanel

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