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