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