001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * ---------------
028     * ChartPanel.java
029     * ---------------
030     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   Andrzej Porebski;
034     *                   Soren Caspersen;
035     *                   Jonathan Nash;
036     *                   Hans-Jurgen Greiner;
037     *                   Andreas Schneider;
038     *                   Daniel van Enckevort;
039     *                   David M O'Donnell;
040     *                   Arnaud Lelievre;
041     *                   Matthias Rose;
042     *                   Onno vd Akker;
043     *                   Sergei Ivanov;
044     *
045     * $Id: ChartPanel.java,v 1.20.2.15 2007/06/06 15:08:23 mungady Exp $
046     *
047     * Changes (from 28-Jun-2001)
048     * --------------------------
049     * 28-Jun-2001 : Integrated buffering code contributed by S???ren 
050     *               Caspersen (DG);
051     * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
052     * 22-Nov-2001 : Added scaling to improve display of charts in small sizes (DG);
053     * 26-Nov-2001 : Added property editing, saving and printing (DG);
054     * 11-Dec-2001 : Transferred saveChartAsPNG method to new ChartUtilities 
055     *               class (DG);
056     * 13-Dec-2001 : Added tooltips (DG);
057     * 16-Jan-2002 : Added an optional crosshair, based on the implementation by 
058     *               Jonathan Nash. Renamed the tooltips class (DG);
059     * 23-Jan-2002 : Implemented zooming based on code by Hans-Jurgen Greiner (DG);
060     * 05-Feb-2002 : Improved tooltips setup.  Renamed method attemptSaveAs() 
061     *               --> doSaveAs() and made it public rather than private (DG);
062     * 28-Mar-2002 : Added a new constructor (DG);
063     * 09-Apr-2002 : Changed initialisation of tooltip generation, as suggested by 
064     *               Hans-Jurgen Greiner (DG);
065     * 27-May-2002 : New interactive zooming methods based on code by Hans-Jurgen 
066     *               Greiner. Renamed JFreeChartPanel --> ChartPanel, moved 
067     *               constants to ChartPanelConstants interface (DG);
068     * 31-May-2002 : Fixed a bug with interactive zooming and added a way to 
069     *               control if the zoom rectangle is filled in or drawn as an 
070     *               outline. A mouse drag gesture towards the top left now causes 
071     *               an autoRangeBoth() and is a way to undo zooms (AS);
072     * 11-Jun-2002 : Reinstated handleClick method call in mouseClicked() to get 
073     *               crosshairs working again (DG);
074     * 13-Jun-2002 : Added check for null popup menu in mouseDragged method (DG);
075     * 18-Jun-2002 : Added get/set methods for minimum and maximum chart 
076     *               dimensions (DG);
077     * 25-Jun-2002 : Removed redundant code (DG);
078     * 27-Aug-2002 : Added get/set methods for popup menu (DG);
079     * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
080     * 22-Oct-2002 : Added translation methods for screen <--> Java2D, contributed
081     *               by Daniel van Enckevort (DG);
082     * 05-Nov-2002 : Added a chart reference to the ChartMouseEvent class (DG);
083     * 22-Nov-2002 : Added test in zoom method for inverted axes, supplied by 
084     *               David M O'Donnell (DG);
085     * 14-Jan-2003 : Implemented ChartProgressListener interface (DG);
086     * 14-Feb-2003 : Removed deprecated setGenerateTooltips method (DG);
087     * 12-Mar-2003 : Added option to enforce filename extension (see bug id 
088     *               643173) (DG);
089     * 08-Sep-2003 : Added internationalization via use of properties 
090     *               resourceBundle (RFE 690236) (AL);
091     * 18-Sep-2003 : Added getScaleX() and getScaleY() methods (protected) as 
092     *               requested by Irv Thomae (DG);
093     * 12-Nov-2003 : Added zooming support for the FastScatterPlot class (DG);
094     * 24-Nov-2003 : Minor Javadoc updates (DG);
095     * 04-Dec-2003 : Added anchor point for crosshair calculation (DG);
096     * 17-Jan-2004 : Added new methods to set tooltip delays to be used in this 
097     *               chart panel. Refer to patch 877565 (MR);
098     * 02-Feb-2004 : Fixed bug in zooming trigger and added zoomTriggerDistance 
099     *               attribute (DG);
100     * 08-Apr-2004 : Changed getScaleX() and getScaleY() from protected to 
101     *               public (DG);
102     * 15-Apr-2004 : Added zoomOutFactor and zoomInFactor (DG);
103     * 21-Apr-2004 : Fixed zooming bug in mouseReleased() method (DG);
104     * 13-Jul-2004 : Added check for null chart (DG);
105     * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG); 
106     * 11-Nov-2004 : Moved constants back in from ChartPanelConstants (DG);
107     * 12-Nov-2004 : Modified zooming mechanism to support zooming within 
108     *               subplots (DG);
109     * 26-Jan-2005 : Fixed mouse zooming for horizontal category plots (DG);
110     * 11-Apr-2005 : Added getFillZoomRectangle() method, renamed 
111     *               setHorizontalZoom() --> setDomainZoomable(), 
112     *               setVerticalZoom() --> setRangeZoomable(), added 
113     *               isDomainZoomable() and isRangeZoomable(), added 
114     *               getHorizontalAxisTrace() and getVerticalAxisTrace(),
115     *               renamed autoRangeBoth() --> restoreAutoBounds(),
116     *               autoRangeHorizontal() --> restoreAutoDomainBounds(),
117     *               autoRangeVertical() --> restoreAutoRangeBounds() (DG);
118     * 12-Apr-2005 : Removed working areas, added getAnchorPoint() method,
119     *               added protected accessors for tracelines (DG);
120     * 18-Apr-2005 : Made constants final (DG);
121     * 26-Apr-2005 : Removed LOGGER (DG);
122     * 01-Jun-2005 : Fixed zooming for combined plots - see bug report 
123     *               1212039, fix thanks to Onno vd Akker (DG);
124     * 25-Nov-2005 : Reworked event listener mechanism (DG);
125     * ------------- JFREECHART 1.0.x ---------------------------------------------
126     * 01-Aug-2006 : Fixed minor bug in restoreAutoRangeBounds() (DG);
127     * 04-Sep-2006 : Renamed attemptEditChartProperties() --> 
128     *               doEditChartProperties() and made public (DG);
129     * 13-Sep-2006 : Don't generate ChartMouseEvents if the panel's chart is null
130     *               (fixes bug 1556951) (DG);
131     * 05-Mar-2007 : Applied patch 1672561 by Sergei Ivanov, to fix zoom rectangle
132     *               drawing for dynamic charts (DG);
133     * 17-Apr-2007 : Fix NullPointerExceptions in zooming for combined plots (DG);
134     * 24-May-2007 : When the look-and-feel changes, update the popup menu if there 
135     *               is one (DG);
136     * 06-Jun-2007 : Fixed coordinates for drawing buffer image (DG);
137     *               
138     */
139    
140    package org.jfree.chart;
141    
142    import java.awt.AWTEvent;
143    import java.awt.Color;
144    import java.awt.Dimension;
145    import java.awt.Graphics;
146    import java.awt.Graphics2D;
147    import java.awt.Image;
148    import java.awt.Insets;
149    import java.awt.Point;
150    import java.awt.event.ActionEvent;
151    import java.awt.event.ActionListener;
152    import java.awt.event.MouseEvent;
153    import java.awt.event.MouseListener;
154    import java.awt.event.MouseMotionListener;
155    import java.awt.geom.AffineTransform;
156    import java.awt.geom.Line2D;
157    import java.awt.geom.Point2D;
158    import java.awt.geom.Rectangle2D;
159    import java.awt.print.PageFormat;
160    import java.awt.print.Printable;
161    import java.awt.print.PrinterException;
162    import java.awt.print.PrinterJob;
163    import java.io.File;
164    import java.io.IOException;
165    import java.io.Serializable;
166    import java.util.EventListener;
167    import java.util.ResourceBundle;
168    
169    import javax.swing.JFileChooser;
170    import javax.swing.JMenu;
171    import javax.swing.JMenuItem;
172    import javax.swing.JOptionPane;
173    import javax.swing.JPanel;
174    import javax.swing.JPopupMenu;
175    import javax.swing.SwingUtilities;
176    import javax.swing.ToolTipManager;
177    import javax.swing.event.EventListenerList;
178    
179    import org.jfree.chart.editor.ChartEditor;
180    import org.jfree.chart.editor.ChartEditorManager;
181    import org.jfree.chart.entity.ChartEntity;
182    import org.jfree.chart.entity.EntityCollection;
183    import org.jfree.chart.event.ChartChangeEvent;
184    import org.jfree.chart.event.ChartChangeListener;
185    import org.jfree.chart.event.ChartProgressEvent;
186    import org.jfree.chart.event.ChartProgressListener;
187    import org.jfree.chart.plot.Plot;
188    import org.jfree.chart.plot.PlotOrientation;
189    import org.jfree.chart.plot.PlotRenderingInfo;
190    import org.jfree.chart.plot.Zoomable;
191    import org.jfree.ui.ExtensionFileFilter;
192    
193    /**
194     * A Swing GUI component for displaying a {@link JFreeChart} object.
195     * <P>
196     * The panel registers with the chart to receive notification of changes to any
197     * component of the chart.  The chart is redrawn automatically whenever this 
198     * notification is received.
199     */
200    public class ChartPanel extends JPanel 
201                            implements ChartChangeListener,
202                                       ChartProgressListener,
203                                       ActionListener,
204                                       MouseListener,
205                                       MouseMotionListener,
206                                       Printable,
207                                       Serializable {
208    
209        /** For serialization. */
210        private static final long serialVersionUID = 6046366297214274674L;
211        
212        /** Default setting for buffer usage. */
213        public static final boolean DEFAULT_BUFFER_USED = false;
214    
215        /** The default panel width. */
216        public static final int DEFAULT_WIDTH = 680;
217    
218        /** The default panel height. */
219        public static final int DEFAULT_HEIGHT = 420;
220    
221        /** The default limit below which chart scaling kicks in. */
222        public static final int DEFAULT_MINIMUM_DRAW_WIDTH = 300;
223    
224        /** The default limit below which chart scaling kicks in. */
225        public static final int DEFAULT_MINIMUM_DRAW_HEIGHT = 200;
226    
227        /** The default limit below which chart scaling kicks in. */
228        public static final int DEFAULT_MAXIMUM_DRAW_WIDTH = 800;
229    
230        /** The default limit below which chart scaling kicks in. */
231        public static final int DEFAULT_MAXIMUM_DRAW_HEIGHT = 600;
232    
233        /** The minimum size required to perform a zoom on a rectangle */
234        public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10;
235    
236        /** Properties action command. */
237        public static final String PROPERTIES_COMMAND = "PROPERTIES";
238    
239        /** Save action command. */
240        public static final String SAVE_COMMAND = "SAVE";
241    
242        /** Print action command. */
243        public static final String PRINT_COMMAND = "PRINT";
244    
245        /** Zoom in (both axes) action command. */
246        public static final String ZOOM_IN_BOTH_COMMAND = "ZOOM_IN_BOTH";
247    
248        /** Zoom in (domain axis only) action command. */
249        public static final String ZOOM_IN_DOMAIN_COMMAND = "ZOOM_IN_DOMAIN";
250    
251        /** Zoom in (range axis only) action command. */
252        public static final String ZOOM_IN_RANGE_COMMAND = "ZOOM_IN_RANGE";
253    
254        /** Zoom out (both axes) action command. */
255        public static final String ZOOM_OUT_BOTH_COMMAND = "ZOOM_OUT_BOTH";
256    
257        /** Zoom out (domain axis only) action command. */
258        public static final String ZOOM_OUT_DOMAIN_COMMAND = "ZOOM_DOMAIN_BOTH";
259    
260        /** Zoom out (range axis only) action command. */
261        public static final String ZOOM_OUT_RANGE_COMMAND = "ZOOM_RANGE_BOTH";
262    
263        /** Zoom reset (both axes) action command. */
264        public static final String ZOOM_RESET_BOTH_COMMAND = "ZOOM_RESET_BOTH";
265    
266        /** Zoom reset (domain axis only) action command. */
267        public static final String ZOOM_RESET_DOMAIN_COMMAND = "ZOOM_RESET_DOMAIN";
268    
269        /** Zoom reset (range axis only) action command. */
270        public static final String ZOOM_RESET_RANGE_COMMAND = "ZOOM_RESET_RANGE";
271    
272        /** The chart that is displayed in the panel. */
273        private JFreeChart chart;
274    
275        /** Storage for registered (chart) mouse listeners. */
276        private EventListenerList chartMouseListeners;
277    
278        /** A flag that controls whether or not the off-screen buffer is used. */
279        private boolean useBuffer;
280    
281        /** A flag that indicates that the buffer should be refreshed. */
282        private boolean refreshBuffer;
283    
284        /** A buffer for the rendered chart. */
285        private Image chartBuffer;
286    
287        /** The height of the chart buffer. */
288        private int chartBufferHeight;
289    
290        /** The width of the chart buffer. */
291        private int chartBufferWidth;
292    
293        /** 
294         * The minimum width for drawing a chart (uses scaling for smaller widths). 
295         */
296        private int minimumDrawWidth;
297    
298        /** 
299         * The minimum height for drawing a chart (uses scaling for smaller 
300         * heights). 
301         */
302        private int minimumDrawHeight;
303    
304        /** 
305         * The maximum width for drawing a chart (uses scaling for bigger 
306         * widths). 
307         */
308        private int maximumDrawWidth;
309    
310        /** 
311         * The maximum height for drawing a chart (uses scaling for bigger 
312         * heights). 
313         */
314        private int maximumDrawHeight;
315    
316        /** The popup menu for the frame. */
317        private JPopupMenu popup;
318    
319        /** The drawing info collected the last time the chart was drawn. */
320        private ChartRenderingInfo info;
321        
322        /** The chart anchor point. */
323        private Point2D anchor;
324    
325        /** The scale factor used to draw the chart. */
326        private double scaleX;
327    
328        /** The scale factor used to draw the chart. */
329        private double scaleY;
330    
331        /** The plot orientation. */
332        private PlotOrientation orientation = PlotOrientation.VERTICAL;
333        
334        /** A flag that controls whether or not domain zooming is enabled. */
335        private boolean domainZoomable = false;
336    
337        /** A flag that controls whether or not range zooming is enabled. */
338        private boolean rangeZoomable = false;
339    
340        /** 
341         * The zoom rectangle starting point (selected by the user with a mouse 
342         * click).  This is a point on the screen, not the chart (which may have
343         * been scaled up or down to fit the panel).  
344         */
345        private Point zoomPoint = null;
346    
347        /** The zoom rectangle (selected by the user with the mouse). */
348        private transient Rectangle2D zoomRectangle = null;
349    
350        /** Controls if the zoom rectangle is drawn as an outline or filled. */
351        private boolean fillZoomRectangle = false;
352    
353        /** The minimum distance required to drag the mouse to trigger a zoom. */
354        private int zoomTriggerDistance;
355        
356        /** A flag that controls whether or not horizontal tracing is enabled. */
357        private boolean horizontalAxisTrace = false;
358    
359        /** A flag that controls whether or not vertical tracing is enabled. */
360        private boolean verticalAxisTrace = false;
361    
362        /** A vertical trace line. */
363        private transient Line2D verticalTraceLine;
364    
365        /** A horizontal trace line. */
366        private transient Line2D horizontalTraceLine;
367    
368        /** Menu item for zooming in on a chart (both axes). */
369        private JMenuItem zoomInBothMenuItem;
370    
371        /** Menu item for zooming in on a chart (domain axis). */
372        private JMenuItem zoomInDomainMenuItem;
373    
374        /** Menu item for zooming in on a chart (range axis). */
375        private JMenuItem zoomInRangeMenuItem;
376    
377        /** Menu item for zooming out on a chart. */
378        private JMenuItem zoomOutBothMenuItem;
379    
380        /** Menu item for zooming out on a chart (domain axis). */
381        private JMenuItem zoomOutDomainMenuItem;
382    
383        /** Menu item for zooming out on a chart (range axis). */
384        private JMenuItem zoomOutRangeMenuItem;
385    
386        /** Menu item for resetting the zoom (both axes). */
387        private JMenuItem zoomResetBothMenuItem;
388    
389        /** Menu item for resetting the zoom (domain axis only). */
390        private JMenuItem zoomResetDomainMenuItem;
391    
392        /** Menu item for resetting the zoom (range axis only). */
393        private JMenuItem zoomResetRangeMenuItem;
394    
395        /** A flag that controls whether or not file extensions are enforced. */
396        private boolean enforceFileExtensions;
397    
398        /** A flag that indicates if original tooltip delays are changed. */
399        private boolean ownToolTipDelaysActive;  
400        
401        /** Original initial tooltip delay of ToolTipManager.sharedInstance(). */
402        private int originalToolTipInitialDelay;
403    
404        /** Original reshow tooltip delay of ToolTipManager.sharedInstance(). */
405        private int originalToolTipReshowDelay;  
406    
407        /** Original dismiss tooltip delay of ToolTipManager.sharedInstance(). */
408        private int originalToolTipDismissDelay;
409    
410        /** Own initial tooltip delay to be used in this chart panel. */
411        private int ownToolTipInitialDelay;
412        
413        /** Own reshow tooltip delay to be used in this chart panel. */
414        private int ownToolTipReshowDelay;  
415    
416        /** Own dismiss tooltip delay to be used in this chart panel. */
417        private int ownToolTipDismissDelay;    
418    
419        /** The factor used to zoom in on an axis range. */
420        private double zoomInFactor = 0.5;
421        
422        /** The factor used to zoom out on an axis range. */
423        private double zoomOutFactor = 2.0;
424        
425        /** The resourceBundle for the localization. */
426        protected static ResourceBundle localizationResources 
427            = ResourceBundle.getBundle("org.jfree.chart.LocalizationBundle");
428    
429        /**
430         * Constructs a panel that displays the specified chart.
431         *
432         * @param chart  the chart.
433         */
434        public ChartPanel(JFreeChart chart) {
435    
436            this(
437                chart,
438                DEFAULT_WIDTH,
439                DEFAULT_HEIGHT,
440                DEFAULT_MINIMUM_DRAW_WIDTH,
441                DEFAULT_MINIMUM_DRAW_HEIGHT,
442                DEFAULT_MAXIMUM_DRAW_WIDTH,
443                DEFAULT_MAXIMUM_DRAW_HEIGHT,
444                DEFAULT_BUFFER_USED,
445                true,  // properties
446                true,  // save
447                true,  // print
448                true,  // zoom
449                true   // tooltips
450            );
451    
452        }
453    
454        /**
455         * Constructs a panel containing a chart.
456         *
457         * @param chart  the chart.
458         * @param useBuffer  a flag controlling whether or not an off-screen buffer
459         *                   is used.
460         */
461        public ChartPanel(JFreeChart chart, boolean useBuffer) {
462    
463            this(chart,
464                 DEFAULT_WIDTH,
465                 DEFAULT_HEIGHT,
466                 DEFAULT_MINIMUM_DRAW_WIDTH,
467                 DEFAULT_MINIMUM_DRAW_HEIGHT,
468                 DEFAULT_MAXIMUM_DRAW_WIDTH,
469                 DEFAULT_MAXIMUM_DRAW_HEIGHT,
470                 useBuffer,
471                 true,  // properties
472                 true,  // save
473                 true,  // print
474                 true,  // zoom
475                 true   // tooltips
476                 );
477    
478        }
479    
480        /**
481         * Constructs a JFreeChart panel.
482         *
483         * @param chart  the chart.
484         * @param properties  a flag indicating whether or not the chart property
485         *                    editor should be available via the popup menu.
486         * @param save  a flag indicating whether or not save options should be
487         *              available via the popup menu.
488         * @param print  a flag indicating whether or not the print option
489         *               should be available via the popup menu.
490         * @param zoom  a flag indicating whether or not zoom options should
491         *              be added to the popup menu.
492         * @param tooltips  a flag indicating whether or not tooltips should be
493         *                  enabled for the chart.
494         */
495        public ChartPanel(JFreeChart chart,
496                          boolean properties,
497                          boolean save,
498                          boolean print,
499                          boolean zoom,
500                          boolean tooltips) {
501    
502            this(chart,
503                 DEFAULT_WIDTH,
504                 DEFAULT_HEIGHT,
505                 DEFAULT_MINIMUM_DRAW_WIDTH,
506                 DEFAULT_MINIMUM_DRAW_HEIGHT,
507                 DEFAULT_MAXIMUM_DRAW_WIDTH,
508                 DEFAULT_MAXIMUM_DRAW_HEIGHT,
509                 DEFAULT_BUFFER_USED,
510                 properties,
511                 save,
512                 print,
513                 zoom,
514                 tooltips
515                 );
516    
517        }
518    
519        /**
520         * Constructs a JFreeChart panel.
521         *
522         * @param chart  the chart.
523         * @param width  the preferred width of the panel.
524         * @param height  the preferred height of the panel.
525         * @param minimumDrawWidth  the minimum drawing width.
526         * @param minimumDrawHeight  the minimum drawing height.
527         * @param maximumDrawWidth  the maximum drawing width.
528         * @param maximumDrawHeight  the maximum drawing height.
529         * @param useBuffer  a flag that indicates whether to use the off-screen
530         *                   buffer to improve performance (at the expense of 
531         *                   memory).
532         * @param properties  a flag indicating whether or not the chart property
533         *                    editor should be available via the popup menu.
534         * @param save  a flag indicating whether or not save options should be
535         *              available via the popup menu.
536         * @param print  a flag indicating whether or not the print option
537         *               should be available via the popup menu.
538         * @param zoom  a flag indicating whether or not zoom options should be 
539         *              added to the popup menu.
540         * @param tooltips  a flag indicating whether or not tooltips should be 
541         *                  enabled for the chart.
542         */
543        public ChartPanel(JFreeChart chart,
544                          int width,
545                          int height,
546                          int minimumDrawWidth,
547                          int minimumDrawHeight,
548                          int maximumDrawWidth,
549                          int maximumDrawHeight,
550                          boolean useBuffer,
551                          boolean properties,
552                          boolean save,
553                          boolean print,
554                          boolean zoom,
555                          boolean tooltips) {
556    
557            this.setChart(chart);
558            this.chartMouseListeners = new EventListenerList();
559            this.info = new ChartRenderingInfo();
560            setPreferredSize(new Dimension(width, height));
561            this.useBuffer = useBuffer;
562            this.refreshBuffer = false;
563            this.minimumDrawWidth = minimumDrawWidth;
564            this.minimumDrawHeight = minimumDrawHeight;
565            this.maximumDrawWidth = maximumDrawWidth;
566            this.maximumDrawHeight = maximumDrawHeight;
567            this.zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE;
568    
569            // set up popup menu...
570            this.popup = null;
571            if (properties || save || print || zoom) {
572                this.popup = createPopupMenu(properties, save, print, zoom);
573            }
574    
575            enableEvents(AWTEvent.MOUSE_EVENT_MASK);
576            enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
577            setDisplayToolTips(tooltips);
578            addMouseListener(this);
579            addMouseMotionListener(this);
580    
581            this.enforceFileExtensions = true;
582    
583            // initialize ChartPanel-specific tool tip delays with
584            // values the from ToolTipManager.sharedInstance()
585            ToolTipManager ttm = ToolTipManager.sharedInstance();       
586            this.ownToolTipInitialDelay = ttm.getInitialDelay();
587            this.ownToolTipDismissDelay = ttm.getDismissDelay();
588            this.ownToolTipReshowDelay = ttm.getReshowDelay();
589    
590        }
591    
592        /**
593         * Returns the chart contained in the panel.
594         *
595         * @return The chart (possibly <code>null</code>).
596         */
597        public JFreeChart getChart() {
598            return this.chart;
599        }
600    
601        /**
602         * Sets the chart that is displayed in the panel.
603         *
604         * @param chart  the chart (<code>null</code> permitted).
605         */
606        public void setChart(JFreeChart chart) {
607    
608            // stop listening for changes to the existing chart
609            if (this.chart != null) {
610                this.chart.removeChangeListener(this);
611                this.chart.removeProgressListener(this);
612            }
613    
614            // add the new chart
615            this.chart = chart;
616            if (chart != null) {
617                this.chart.addChangeListener(this);
618                this.chart.addProgressListener(this);
619                Plot plot = chart.getPlot();
620                this.domainZoomable = false;
621                this.rangeZoomable = false;
622                if (plot instanceof Zoomable) {
623                    Zoomable z = (Zoomable) plot;
624                    this.domainZoomable = z.isDomainZoomable();
625                    this.rangeZoomable = z.isRangeZoomable();
626                    this.orientation = z.getOrientation();
627                }
628            }
629            else {
630                this.domainZoomable = false;
631                this.rangeZoomable = false;
632            }
633            if (this.useBuffer) {
634                this.refreshBuffer = true;
635            }
636            repaint();
637    
638        }
639    
640        /**
641         * Returns the minimum drawing width for charts.
642         * <P>
643         * If the width available on the panel is less than this, then the chart is
644         * drawn at the minimum width then scaled down to fit.
645         *
646         * @return The minimum drawing width.
647         */
648        public int getMinimumDrawWidth() {
649            return this.minimumDrawWidth;
650        }
651    
652        /**
653         * Sets the minimum drawing width for the chart on this panel.
654         * <P>
655         * At the time the chart is drawn on the panel, if the available width is
656         * less than this amount, the chart will be drawn using the minimum width
657         * then scaled down to fit the available space.
658         *
659         * @param width  The width.
660         */
661        public void setMinimumDrawWidth(int width) {
662            this.minimumDrawWidth = width;
663        }
664    
665        /**
666         * Returns the maximum drawing width for charts.
667         * <P>
668         * If the width available on the panel is greater than this, then the chart
669         * is drawn at the maximum width then scaled up to fit.
670         *
671         * @return The maximum drawing width.
672         */
673        public int getMaximumDrawWidth() {
674            return this.maximumDrawWidth;
675        }
676    
677        /**
678         * Sets the maximum drawing width for the chart on this panel.
679         * <P>
680         * At the time the chart is drawn on the panel, if the available width is
681         * greater than this amount, the chart will be drawn using the maximum
682         * width then scaled up to fit the available space.
683         *
684         * @param width  The width.
685         */
686        public void setMaximumDrawWidth(int width) {
687            this.maximumDrawWidth = width;
688        }
689    
690        /**
691         * Returns the minimum drawing height for charts.
692         * <P>
693         * If the height available on the panel is less than this, then the chart
694         * is drawn at the minimum height then scaled down to fit.
695         *
696         * @return The minimum drawing height.
697         */
698        public int getMinimumDrawHeight() {
699            return this.minimumDrawHeight;
700        }
701    
702        /**
703         * Sets the minimum drawing height for the chart on this panel.
704         * <P>
705         * At the time the chart is drawn on the panel, if the available height is
706         * less than this amount, the chart will be drawn using the minimum height
707         * then scaled down to fit the available space.
708         *
709         * @param height  The height.
710         */
711        public void setMinimumDrawHeight(int height) {
712            this.minimumDrawHeight = height;
713        }
714    
715        /**
716         * Returns the maximum drawing height for charts.
717         * <P>
718         * If the height available on the panel is greater than this, then the
719         * chart is drawn at the maximum height then scaled up to fit.
720         *
721         * @return The maximum drawing height.
722         */
723        public int getMaximumDrawHeight() {
724            return this.maximumDrawHeight;
725        }
726    
727        /**
728         * Sets the maximum drawing height for the chart on this panel.
729         * <P>
730         * At the time the chart is drawn on the panel, if the available height is
731         * greater than this amount, the chart will be drawn using the maximum
732         * height then scaled up to fit the available space.
733         *
734         * @param height  The height.
735         */
736        public void setMaximumDrawHeight(int height) {
737            this.maximumDrawHeight = height;
738        }
739    
740        /**
741         * Returns the X scale factor for the chart.  This will be 1.0 if no 
742         * scaling has been used.
743         * 
744         * @return The scale factor.
745         */
746        public double getScaleX() {
747            return this.scaleX;
748        }
749        
750        /**
751         * Returns the Y scale factory for the chart.  This will be 1.0 if no 
752         * scaling has been used.
753         * 
754         * @return The scale factor.
755         */
756        public double getScaleY() {
757            return this.scaleY;
758        }
759        
760        /**
761         * Returns the anchor point.
762         * 
763         * @return The anchor point (possibly <code>null</code>).
764         */
765        public Point2D getAnchor() {
766            return this.anchor;   
767        }
768        
769        /**
770         * Sets the anchor point.  This method is provided for the use of 
771         * subclasses, not end users.
772         * 
773         * @param anchor  the anchor point (<code>null</code> permitted).
774         */
775        protected void setAnchor(Point2D anchor) {
776            this.anchor = anchor;   
777        }
778        
779        /**
780         * Returns the popup menu.
781         *
782         * @return The popup menu.
783         */
784        public JPopupMenu getPopupMenu() {
785            return this.popup;
786        }
787    
788        /**
789         * Sets the popup menu for the panel.
790         *
791         * @param popup  the popup menu (<code>null</code> permitted).
792         */
793        public void setPopupMenu(JPopupMenu popup) {
794            this.popup = popup;
795        }
796    
797        /**
798         * Returns the chart rendering info from the most recent chart redraw.
799         *
800         * @return The chart rendering info.
801         */
802        public ChartRenderingInfo getChartRenderingInfo() {
803            return this.info;
804        }
805    
806        /**
807         * A convenience method that switches on mouse-based zooming.
808         *
809         * @param flag  <code>true</code> enables zooming and rectangle fill on 
810         *              zoom.
811         */
812        public void setMouseZoomable(boolean flag) {
813            setMouseZoomable(flag, true);
814        }
815    
816        /**
817         * A convenience method that switches on mouse-based zooming.
818         *
819         * @param flag  <code>true</code> if zooming enabled
820         * @param fillRectangle  <code>true</code> if zoom rectangle is filled,
821         *                       false if rectangle is shown as outline only.
822         */
823        public void setMouseZoomable(boolean flag, boolean fillRectangle) {
824            setDomainZoomable(flag);
825            setRangeZoomable(flag);
826            setFillZoomRectangle(fillRectangle);
827        }
828    
829        /**
830         * Returns the flag that determines whether or not zooming is enabled for 
831         * the domain axis.
832         * 
833         * @return A boolean.
834         */
835        public boolean isDomainZoomable() {
836            return this.domainZoomable;
837        }
838        
839        /**
840         * Sets the flag that controls whether or not zooming is enable for the 
841         * domain axis.  A check is made to ensure that the current plot supports
842         * zooming for the domain values.
843         *
844         * @param flag  <code>true</code> enables zooming if possible.
845         */
846        public void setDomainZoomable(boolean flag) {
847            if (flag) {
848                Plot plot = this.chart.getPlot();
849                if (plot instanceof Zoomable) {
850                    Zoomable z = (Zoomable) plot;
851                    this.domainZoomable = flag && (z.isDomainZoomable());  
852                }
853            }
854            else {
855                this.domainZoomable = false;
856            }
857        }
858    
859        /**
860         * Returns the flag that determines whether or not zooming is enabled for 
861         * the range axis.
862         * 
863         * @return A boolean.
864         */
865        public boolean isRangeZoomable() {
866            return this.rangeZoomable;
867        }
868        
869        /**
870         * A flag that controls mouse-based zooming on the vertical axis.
871         *
872         * @param flag  <code>true</code> enables zooming.
873         */
874        public void setRangeZoomable(boolean flag) {
875            if (flag) {
876                Plot plot = this.chart.getPlot();
877                if (plot instanceof Zoomable) {
878                    Zoomable z = (Zoomable) plot;
879                    this.rangeZoomable = flag && (z.isRangeZoomable());  
880                }
881            }
882            else {
883                this.rangeZoomable = false;
884            }
885        }
886    
887        /**
888         * Returns the flag that controls whether or not the zoom rectangle is
889         * filled when drawn.
890         * 
891         * @return A boolean.
892         */
893        public boolean getFillZoomRectangle() {
894            return this.fillZoomRectangle;
895        }
896        
897        /**
898         * A flag that controls how the zoom rectangle is drawn.
899         *
900         * @param flag  <code>true</code> instructs to fill the rectangle on
901         *              zoom, otherwise it will be outlined.
902         */
903        public void setFillZoomRectangle(boolean flag) {
904            this.fillZoomRectangle = flag;
905        }
906    
907        /**
908         * Returns the zoom trigger distance.  This controls how far the mouse must
909         * move before a zoom action is triggered.
910         * 
911         * @return The distance (in Java2D units).
912         */
913        public int getZoomTriggerDistance() {
914            return this.zoomTriggerDistance;
915        }
916        
917        /**
918         * Sets the zoom trigger distance.  This controls how far the mouse must 
919         * move before a zoom action is triggered.
920         * 
921         * @param distance  the distance (in Java2D units).
922         */
923        public void setZoomTriggerDistance(int distance) {
924            this.zoomTriggerDistance = distance;
925        }
926        
927        /**
928         * Returns the flag that controls whether or not a horizontal axis trace
929         * line is drawn over the plot area at the current mouse location.
930         * 
931         * @return A boolean.
932         */
933        public boolean getHorizontalAxisTrace() {
934            return this.horizontalAxisTrace;    
935        }
936        
937        /**
938         * A flag that controls trace lines on the horizontal axis.
939         *
940         * @param flag  <code>true</code> enables trace lines for the mouse
941         *      pointer on the horizontal axis.
942         */
943        public void setHorizontalAxisTrace(boolean flag) {
944            this.horizontalAxisTrace = flag;
945        }
946        
947        /**
948         * Returns the horizontal trace line.
949         * 
950         * @return The horizontal trace line (possibly <code>null</code>).
951         */
952        protected Line2D getHorizontalTraceLine() {
953            return this.horizontalTraceLine;   
954        }
955        
956        /**
957         * Sets the horizontal trace line.
958         * 
959         * @param line  the line (<code>null</code> permitted).
960         */
961        protected void setHorizontalTraceLine(Line2D line) {
962            this.horizontalTraceLine = line;   
963        }
964    
965        /**
966         * Returns the flag that controls whether or not a vertical axis trace
967         * line is drawn over the plot area at the current mouse location.
968         * 
969         * @return A boolean.
970         */
971        public boolean getVerticalAxisTrace() {
972            return this.verticalAxisTrace;    
973        }
974        
975        /**
976         * A flag that controls trace lines on the vertical axis.
977         *
978         * @param flag  <code>true</code> enables trace lines for the mouse
979         *              pointer on the vertical axis.
980         */
981        public void setVerticalAxisTrace(boolean flag) {
982            this.verticalAxisTrace = flag;
983        }
984    
985        /**
986         * Returns the vertical trace line.
987         * 
988         * @return The vertical trace line (possibly <code>null</code>).
989         */
990        protected Line2D getVerticalTraceLine() {
991            return this.verticalTraceLine;   
992        }
993        
994        /**
995         * Sets the vertical trace line.
996         * 
997         * @param line  the line (<code>null</code> permitted).
998         */
999        protected void setVerticalTraceLine(Line2D line) {
1000            this.verticalTraceLine = line;   
1001        }
1002    
1003        /**
1004         * Returns <code>true</code> if file extensions should be enforced, and 
1005         * <code>false</code> otherwise.
1006         *
1007         * @return The flag.
1008         */
1009        public boolean isEnforceFileExtensions() {
1010            return this.enforceFileExtensions;
1011        }
1012    
1013        /**
1014         * Sets a flag that controls whether or not file extensions are enforced.
1015         *
1016         * @param enforce  the new flag value.
1017         */
1018        public void setEnforceFileExtensions(boolean enforce) {
1019            this.enforceFileExtensions = enforce;
1020        }
1021    
1022        /**
1023         * Switches the display of tooltips for the panel on or off.  Note that 
1024         * tooltips can only be displayed if the chart has been configured to
1025         * generate tooltip items.
1026         *
1027         * @param flag  <code>true</code> to enable tooltips, <code>false</code> to
1028         *              disable tooltips.
1029         */
1030        public void setDisplayToolTips(boolean flag) {
1031            if (flag) {
1032                ToolTipManager.sharedInstance().registerComponent(this);
1033            }
1034            else {
1035                ToolTipManager.sharedInstance().unregisterComponent(this);
1036            }
1037        }
1038    
1039        /**
1040         * Returns a string for the tooltip.
1041         *
1042         * @param e  the mouse event.
1043         *
1044         * @return A tool tip or <code>null</code> if no tooltip is available.
1045         */
1046        public String getToolTipText(MouseEvent e) {
1047    
1048            String result = null;
1049            if (this.info != null) {
1050                EntityCollection entities = this.info.getEntityCollection();
1051                if (entities != null) {
1052                    Insets insets = getInsets();
1053                    ChartEntity entity = entities.getEntity(
1054                            (int) ((e.getX() - insets.left) / this.scaleX),
1055                            (int) ((e.getY() - insets.top) / this.scaleY));
1056                    if (entity != null) {
1057                        result = entity.getToolTipText();
1058                    }
1059                }
1060            }
1061            return result;
1062    
1063        }
1064    
1065        /**
1066         * Translates a Java2D point on the chart to a screen location.
1067         *
1068         * @param java2DPoint  the Java2D point.
1069         *
1070         * @return The screen location.
1071         */
1072        public Point translateJava2DToScreen(Point2D java2DPoint) {
1073            Insets insets = getInsets();
1074            int x = (int) (java2DPoint.getX() * this.scaleX + insets.left);
1075            int y = (int) (java2DPoint.getY() * this.scaleY + insets.top);
1076            return new Point(x, y);
1077        }
1078    
1079        /**
1080         * Translates a screen location to a Java2D point.
1081         *
1082         * @param screenPoint  the screen location.
1083         *
1084         * @return The Java2D coordinates.
1085         */
1086        public Point2D translateScreenToJava2D(Point screenPoint) {
1087            Insets insets = getInsets();
1088            double x = (screenPoint.getX() - insets.left) / this.scaleX;
1089            double y = (screenPoint.getY() - insets.top) / this.scaleY;
1090            return new Point2D.Double(x, y);
1091        }
1092    
1093        /**
1094         * Applies any scaling that is in effect for the chart drawing to the
1095         * given rectangle.
1096         *  
1097         * @param rect  the rectangle.
1098         * 
1099         * @return A new scaled rectangle.
1100         */
1101        public Rectangle2D scale(Rectangle2D rect) {
1102            Insets insets = getInsets();
1103            double x = rect.getX() * getScaleX() + insets.left;
1104            double y = rect.getY() * this.getScaleY() + insets.top;
1105            double w = rect.getWidth() * this.getScaleX();
1106            double h = rect.getHeight() * this.getScaleY();
1107            return new Rectangle2D.Double(x, y, w, h);
1108        }
1109    
1110        /**
1111         * Returns the chart entity at a given point.
1112         * <P>
1113         * This method will return null if there is (a) no entity at the given 
1114         * point, or (b) no entity collection has been generated.
1115         *
1116         * @param viewX  the x-coordinate.
1117         * @param viewY  the y-coordinate.
1118         *
1119         * @return The chart entity (possibly <code>null</code>).
1120         */
1121        public ChartEntity getEntityForPoint(int viewX, int viewY) {
1122    
1123            ChartEntity result = null;
1124            if (this.info != null) {
1125                Insets insets = getInsets();
1126                double x = (viewX - insets.left) / this.scaleX;
1127                double y = (viewY - insets.top) / this.scaleY;
1128                EntityCollection entities = this.info.getEntityCollection();
1129                result = entities != null ? entities.getEntity(x, y) : null; 
1130            }
1131            return result;
1132    
1133        }
1134    
1135        /**
1136         * Returns the flag that controls whether or not the offscreen buffer
1137         * needs to be refreshed.
1138         * 
1139         * @return A boolean.
1140         */
1141        public boolean getRefreshBuffer() {
1142            return this.refreshBuffer;
1143        }
1144        
1145        /**
1146         * Sets the refresh buffer flag.  This flag is used to avoid unnecessary
1147         * redrawing of the chart when the offscreen image buffer is used.
1148         *
1149         * @param flag  <code>true</code> indicates that the buffer should be 
1150         *              refreshed.
1151         */
1152        public void setRefreshBuffer(boolean flag) {
1153            this.refreshBuffer = flag;
1154        }
1155    
1156        /**
1157         * Paints the component by drawing the chart to fill the entire component,
1158         * but allowing for the insets (which will be non-zero if a border has been
1159         * set for this component).  To increase performance (at the expense of
1160         * memory), an off-screen buffer image can be used.
1161         *
1162         * @param g  the graphics device for drawing on.
1163         */
1164        public void paintComponent(Graphics g) {
1165            super.paintComponent(g);
1166            if (this.chart == null) {
1167                return;
1168            }
1169            Graphics2D g2 = (Graphics2D) g.create();
1170    
1171            // first determine the size of the chart rendering area...
1172            Dimension size = getSize();
1173            Insets insets = getInsets();
1174            Rectangle2D available = new Rectangle2D.Double(insets.left, insets.top,
1175                    size.getWidth() - insets.left - insets.right,
1176                    size.getHeight() - insets.top - insets.bottom);
1177    
1178            // work out if scaling is required...
1179            boolean scale = false;
1180            double drawWidth = available.getWidth();
1181            double drawHeight = available.getHeight();
1182            this.scaleX = 1.0;
1183            this.scaleY = 1.0;
1184    
1185            if (drawWidth < this.minimumDrawWidth) {
1186                this.scaleX = drawWidth / this.minimumDrawWidth;
1187                drawWidth = this.minimumDrawWidth;
1188                scale = true;
1189            }
1190            else if (drawWidth > this.maximumDrawWidth) {
1191                this.scaleX = drawWidth / this.maximumDrawWidth;
1192                drawWidth = this.maximumDrawWidth;
1193                scale = true;
1194            }
1195    
1196            if (drawHeight < this.minimumDrawHeight) {
1197                this.scaleY = drawHeight / this.minimumDrawHeight;
1198                drawHeight = this.minimumDrawHeight;
1199                scale = true;
1200            }
1201            else if (drawHeight > this.maximumDrawHeight) {
1202                this.scaleY = drawHeight / this.maximumDrawHeight;
1203                drawHeight = this.maximumDrawHeight;
1204                scale = true;
1205            }
1206    
1207            Rectangle2D chartArea = new Rectangle2D.Double(0.0, 0.0, drawWidth, 
1208                    drawHeight);
1209    
1210            // are we using the chart buffer?
1211            if (this.useBuffer) {
1212    
1213                // do we need to resize the buffer?
1214                if ((this.chartBuffer == null) 
1215                        || (this.chartBufferWidth != available.getWidth())
1216                        || (this.chartBufferHeight != available.getHeight())
1217                ) {
1218                    this.chartBufferWidth = (int) available.getWidth();
1219                    this.chartBufferHeight = (int) available.getHeight();
1220                    this.chartBuffer = createImage(
1221                            this.chartBufferWidth, this.chartBufferHeight);
1222    //                GraphicsConfiguration gc = g2.getDeviceConfiguration();
1223    //                this.chartBuffer = gc.createCompatibleImage(
1224    //                        this.chartBufferWidth, this.chartBufferHeight, 
1225    //                        Transparency.TRANSLUCENT);
1226                    this.refreshBuffer = true;
1227                }
1228    
1229                // do we need to redraw the buffer?
1230                if (this.refreshBuffer) {
1231    
1232                    Rectangle2D bufferArea = new Rectangle2D.Double(
1233                            0, 0, this.chartBufferWidth, this.chartBufferHeight);
1234    
1235                    Graphics2D bufferG2 
1236                        = (Graphics2D) this.chartBuffer.getGraphics();
1237                    if (scale) {
1238                        AffineTransform saved = bufferG2.getTransform();
1239                        AffineTransform st = AffineTransform.getScaleInstance(
1240                                this.scaleX, this.scaleY);
1241                        bufferG2.transform(st);
1242                        this.chart.draw(bufferG2, chartArea, this.anchor, 
1243                                this.info);
1244                        bufferG2.setTransform(saved);
1245                    }
1246                    else {
1247                        this.chart.draw(bufferG2, bufferArea, this.anchor, 
1248                                this.info);
1249                    }
1250    
1251                    this.refreshBuffer = false;
1252    
1253                }
1254    
1255                // zap the buffer onto the panel...
1256                g2.drawImage(this.chartBuffer, insets.left, insets.top, this);
1257    
1258            }
1259    
1260            // or redrawing the chart every time...
1261            else {
1262    
1263                AffineTransform saved = g2.getTransform();
1264                g2.translate(insets.left, insets.top);
1265                if (scale) {
1266                    AffineTransform st = AffineTransform.getScaleInstance(
1267                            this.scaleX, this.scaleY);
1268                    g2.transform(st);
1269                }
1270                this.chart.draw(g2, chartArea, this.anchor, this.info);
1271                g2.setTransform(saved);
1272    
1273            }
1274            
1275            // Redraw the zoom rectangle (if present)
1276            drawZoomRectangle(g2);
1277            
1278            g2.dispose();
1279    
1280            this.anchor = null;
1281            this.verticalTraceLine = null;
1282            this.horizontalTraceLine = null;
1283    
1284        }
1285    
1286        /**
1287         * Receives notification of changes to the chart, and redraws the chart.
1288         *
1289         * @param event  details of the chart change event.
1290         */
1291        public void chartChanged(ChartChangeEvent event) {
1292            this.refreshBuffer = true;
1293            Plot plot = this.chart.getPlot();
1294            if (plot instanceof Zoomable) {
1295                Zoomable z = (Zoomable) plot;
1296                this.orientation = z.getOrientation();
1297            }
1298            repaint();
1299        }
1300    
1301        /**
1302         * Receives notification of a chart progress event.
1303         *
1304         * @param event  the event.
1305         */
1306        public void chartProgress(ChartProgressEvent event) {
1307            // does nothing - override if necessary
1308        }
1309    
1310        /**
1311         * Handles action events generated by the popup menu.
1312         *
1313         * @param event  the event.
1314         */
1315        public void actionPerformed(ActionEvent event) {
1316    
1317            String command = event.getActionCommand();
1318    
1319            // many of the zoom methods need a screen location - all we have is 
1320            // the zoomPoint, but it might be null.  Here we grab the x and y
1321            // coordinates, or use defaults...
1322            double screenX = -1.0;
1323            double screenY = -1.0;
1324            if (this.zoomPoint != null) {
1325                screenX = this.zoomPoint.getX();
1326                screenY = this.zoomPoint.getY();
1327            }
1328            
1329            if (command.equals(PROPERTIES_COMMAND)) {
1330                doEditChartProperties();
1331            }
1332            else if (command.equals(SAVE_COMMAND)) {
1333                try {
1334                    doSaveAs();
1335                }
1336                catch (IOException e) {
1337                    e.printStackTrace();
1338                }
1339            }
1340            else if (command.equals(PRINT_COMMAND)) {
1341                createChartPrintJob();
1342            }
1343            else if (command.equals(ZOOM_IN_BOTH_COMMAND)) {
1344                zoomInBoth(screenX, screenY);
1345            }
1346            else if (command.equals(ZOOM_IN_DOMAIN_COMMAND)) {
1347                zoomInDomain(screenX, screenY);
1348            }
1349            else if (command.equals(ZOOM_IN_RANGE_COMMAND)) {
1350                zoomInRange(screenX, screenY);
1351            }
1352            else if (command.equals(ZOOM_OUT_BOTH_COMMAND)) {
1353                zoomOutBoth(screenX, screenY);
1354            }
1355            else if (command.equals(ZOOM_OUT_DOMAIN_COMMAND)) {
1356                zoomOutDomain(screenX, screenY);
1357            }
1358            else if (command.equals(ZOOM_OUT_RANGE_COMMAND)) {
1359                zoomOutRange(screenX, screenY);
1360            }
1361            else if (command.equals(ZOOM_RESET_BOTH_COMMAND)) {
1362                restoreAutoBounds();
1363            }
1364            else if (command.equals(ZOOM_RESET_DOMAIN_COMMAND)) {
1365                restoreAutoDomainBounds();
1366            }
1367            else if (command.equals(ZOOM_RESET_RANGE_COMMAND)) {
1368                restoreAutoRangeBounds();
1369            }
1370    
1371        }
1372    
1373        /**
1374         * Handles a 'mouse entered' event. This method changes the tooltip delays
1375         * of ToolTipManager.sharedInstance() to the possibly different values set 
1376         * for this chart panel. 
1377         *
1378         * @param e  the mouse event.
1379         */
1380        public void mouseEntered(MouseEvent e) {
1381            if (!this.ownToolTipDelaysActive) {
1382                ToolTipManager ttm = ToolTipManager.sharedInstance();
1383                
1384                this.originalToolTipInitialDelay = ttm.getInitialDelay();
1385                ttm.setInitialDelay(this.ownToolTipInitialDelay);
1386        
1387                this.originalToolTipReshowDelay = ttm.getReshowDelay();
1388                ttm.setReshowDelay(this.ownToolTipReshowDelay);
1389                
1390                this.originalToolTipDismissDelay = ttm.getDismissDelay();
1391                ttm.setDismissDelay(this.ownToolTipDismissDelay);
1392        
1393                this.ownToolTipDelaysActive = true;
1394            }
1395        }
1396    
1397        /**
1398         * Handles a 'mouse exited' event. This method resets the tooltip delays of
1399         * ToolTipManager.sharedInstance() to their
1400         * original values in effect before mouseEntered()
1401         *
1402         * @param e  the mouse event.
1403         */
1404        public void mouseExited(MouseEvent e) {
1405            if (this.ownToolTipDelaysActive) {
1406                // restore original tooltip dealys 
1407                ToolTipManager ttm = ToolTipManager.sharedInstance();       
1408                ttm.setInitialDelay(this.originalToolTipInitialDelay);
1409                ttm.setReshowDelay(this.originalToolTipReshowDelay);
1410                ttm.se