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;