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