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