001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * -------------- 028 * Crosshair.java 029 * -------------- 030 * (C) Copyright 2009-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.chart.plot; 038 039import java.awt.BasicStroke; 040import java.awt.Color; 041import java.awt.Font; 042import java.awt.Paint; 043import java.awt.Stroke; 044import java.beans.PropertyChangeListener; 045import java.beans.PropertyChangeSupport; 046import java.io.IOException; 047import java.io.ObjectInputStream; 048import java.io.ObjectOutputStream; 049import java.io.Serializable; 050import org.jfree.chart.HashUtils; 051import org.jfree.chart.labels.CrosshairLabelGenerator; 052import org.jfree.chart.labels.StandardCrosshairLabelGenerator; 053import org.jfree.chart.ui.RectangleAnchor; 054import org.jfree.chart.util.PaintUtils; 055import org.jfree.chart.util.Args; 056import org.jfree.chart.util.PublicCloneable; 057import org.jfree.chart.util.SerialUtils; 058 059/** 060 * A crosshair for display on a plot. 061 */ 062public class Crosshair implements Cloneable, PublicCloneable, Serializable { 063 064 /** Flag controlling visibility. */ 065 private boolean visible; 066 067 /** The crosshair value. */ 068 private double value; 069 070 /** The paint for the crosshair line. */ 071 private transient Paint paint; 072 073 /** The stroke for the crosshair line. */ 074 private transient Stroke stroke; 075 076 /** 077 * A flag that controls whether or not the crosshair has a label 078 * visible. 079 */ 080 private boolean labelVisible; 081 082 /** 083 * The label anchor. 084 */ 085 private RectangleAnchor labelAnchor; 086 087 /** A label generator. */ 088 private CrosshairLabelGenerator labelGenerator; 089 090 /** 091 * The x-offset in Java2D units. 092 */ 093 private double labelXOffset; 094 095 /** 096 * The y-offset in Java2D units. 097 */ 098 private double labelYOffset; 099 100 /** 101 * The label font. 102 */ 103 private Font labelFont; 104 105 /** 106 * The label paint. 107 */ 108 private transient Paint labelPaint; 109 110 /** 111 * The label background paint. 112 */ 113 private transient Paint labelBackgroundPaint; 114 115 /** A flag that controls the visibility of the label outline. */ 116 private boolean labelOutlineVisible; 117 118 /** The label outline stroke. */ 119 private transient Stroke labelOutlineStroke; 120 121 /** The label outline paint. */ 122 private transient Paint labelOutlinePaint; 123 124 /** Property change support. */ 125 private transient PropertyChangeSupport pcs; 126 127 /** 128 * Creates a new crosshair with value 0.0. 129 */ 130 public Crosshair() { 131 this(0.0); 132 } 133 134 /** 135 * Creates a new crosshair with the specified value. 136 * 137 * @param value the value. 138 */ 139 public Crosshair(double value) { 140 this(value, Color.BLACK, new BasicStroke(1.0f)); 141 } 142 143 /** 144 * Creates a new crosshair value with the specified value and line style. 145 * 146 * @param value the value. 147 * @param paint the line paint ({@code null} not permitted). 148 * @param stroke the line stroke ({@code null} not permitted). 149 */ 150 public Crosshair(double value, Paint paint, Stroke stroke) { 151 Args.nullNotPermitted(paint, "paint"); 152 Args.nullNotPermitted(stroke, "stroke"); 153 this.visible = true; 154 this.value = value; 155 this.paint = paint; 156 this.stroke = stroke; 157 this.labelVisible = false; 158 this.labelGenerator = new StandardCrosshairLabelGenerator(); 159 this.labelAnchor = RectangleAnchor.BOTTOM_LEFT; 160 this.labelXOffset = 3.0; 161 this.labelYOffset = 3.0; 162 this.labelFont = new Font("Tahoma", Font.PLAIN, 12); 163 this.labelPaint = Color.BLACK; 164 this.labelBackgroundPaint = new Color(0, 0, 255, 63); 165 this.labelOutlineVisible = true; 166 this.labelOutlinePaint = Color.BLACK; 167 this.labelOutlineStroke = new BasicStroke(0.5f); 168 this.pcs = new PropertyChangeSupport(this); 169 } 170 171 /** 172 * Returns the flag that indicates whether or not the crosshair is 173 * currently visible. 174 * 175 * @return A boolean. 176 * 177 * @see #setVisible(boolean) 178 */ 179 public boolean isVisible() { 180 return this.visible; 181 } 182 183 /** 184 * Sets the flag that controls the visibility of the crosshair and sends 185 * a proerty change event (with the name 'visible') to all registered 186 * listeners. 187 * 188 * @param visible the new flag value. 189 * 190 * @see #isVisible() 191 */ 192 public void setVisible(boolean visible) { 193 boolean old = this.visible; 194 this.visible = visible; 195 this.pcs.firePropertyChange("visible", old, visible); 196 } 197 198 /** 199 * Returns the crosshair value. 200 * 201 * @return The crosshair value. 202 * 203 * @see #setValue(double) 204 */ 205 public double getValue() { 206 return this.value; 207 } 208 209 /** 210 * Sets the crosshair value and sends a property change event with the name 211 * 'value' to all registered listeners. 212 * 213 * @param value the value. 214 * 215 * @see #getValue() 216 */ 217 public void setValue(double value) { 218 Double oldValue = this.value; 219 this.value = value; 220 this.pcs.firePropertyChange("value", oldValue, value); 221 } 222 223 /** 224 * Returns the paint for the crosshair line. 225 * 226 * @return The paint (never {@code null}). 227 * 228 * @see #setPaint(java.awt.Paint) 229 */ 230 public Paint getPaint() { 231 return this.paint; 232 } 233 234 /** 235 * Sets the paint for the crosshair line and sends a property change event 236 * with the name "paint" to all registered listeners. 237 * 238 * @param paint the paint ({@code null} not permitted). 239 * 240 * @see #getPaint() 241 */ 242 public void setPaint(Paint paint) { 243 Paint old = this.paint; 244 this.paint = paint; 245 this.pcs.firePropertyChange("paint", old, paint); 246 } 247 248 /** 249 * Returns the stroke for the crosshair line. 250 * 251 * @return The stroke (never {@code null}). 252 * 253 * @see #setStroke(java.awt.Stroke) 254 */ 255 public Stroke getStroke() { 256 return this.stroke; 257 } 258 259 /** 260 * Sets the stroke for the crosshair line and sends a property change event 261 * with the name "stroke" to all registered listeners. 262 * 263 * @param stroke the stroke ({@code null} not permitted). 264 * 265 * @see #getStroke() 266 */ 267 public void setStroke(Stroke stroke) { 268 Stroke old = this.stroke; 269 this.stroke = stroke; 270 this.pcs.firePropertyChange("stroke", old, stroke); 271 } 272 273 /** 274 * Returns the flag that controls whether or not a label is drawn for 275 * this crosshair. 276 * 277 * @return A boolean. 278 * 279 * @see #setLabelVisible(boolean) 280 */ 281 public boolean isLabelVisible() { 282 return this.labelVisible; 283 } 284 285 /** 286 * Sets the flag that controls whether or not a label is drawn for the 287 * crosshair and sends a property change event (with the name 288 * 'labelVisible') to all registered listeners. 289 * 290 * @param visible the new flag value. 291 * 292 * @see #isLabelVisible() 293 */ 294 public void setLabelVisible(boolean visible) { 295 boolean old = this.labelVisible; 296 this.labelVisible = visible; 297 this.pcs.firePropertyChange("labelVisible", old, visible); 298 } 299 300 /** 301 * Returns the crosshair label generator. 302 * 303 * @return The label crosshair generator (never {@code null}). 304 * 305 * @see #setLabelGenerator(org.jfree.chart.labels.CrosshairLabelGenerator) 306 */ 307 public CrosshairLabelGenerator getLabelGenerator() { 308 return this.labelGenerator; 309 } 310 311 /** 312 * Sets the crosshair label generator and sends a property change event 313 * (with the name 'labelGenerator') to all registered listeners. 314 * 315 * @param generator the new generator ({@code null} not permitted). 316 * 317 * @see #getLabelGenerator() 318 */ 319 public void setLabelGenerator(CrosshairLabelGenerator generator) { 320 Args.nullNotPermitted(generator, "generator"); 321 CrosshairLabelGenerator old = this.labelGenerator; 322 this.labelGenerator = generator; 323 this.pcs.firePropertyChange("labelGenerator", old, generator); 324 } 325 326 /** 327 * Returns the label anchor point. 328 * 329 * @return the label anchor point (never {@code null}. 330 * 331 * @see #setLabelAnchor(org.jfree.chart.ui.RectangleAnchor) 332 */ 333 public RectangleAnchor getLabelAnchor() { 334 return this.labelAnchor; 335 } 336 337 /** 338 * Sets the label anchor point and sends a property change event (with the 339 * name 'labelAnchor') to all registered listeners. 340 * 341 * @param anchor the anchor ({@code null} not permitted). 342 * 343 * @see #getLabelAnchor() 344 */ 345 public void setLabelAnchor(RectangleAnchor anchor) { 346 RectangleAnchor old = this.labelAnchor; 347 this.labelAnchor = anchor; 348 this.pcs.firePropertyChange("labelAnchor", old, anchor); 349 } 350 351 /** 352 * Returns the x-offset for the label (in Java2D units). 353 * 354 * @return The x-offset. 355 * 356 * @see #setLabelXOffset(double) 357 */ 358 public double getLabelXOffset() { 359 return this.labelXOffset; 360 } 361 362 /** 363 * Sets the x-offset and sends a property change event (with the name 364 * 'labelXOffset') to all registered listeners. 365 * 366 * @param offset the new offset. 367 * 368 * @see #getLabelXOffset() 369 */ 370 public void setLabelXOffset(double offset) { 371 Double old = this.labelXOffset; 372 this.labelXOffset = offset; 373 this.pcs.firePropertyChange("labelXOffset", old, offset); 374 } 375 376 /** 377 * Returns the y-offset for the label (in Java2D units). 378 * 379 * @return The y-offset. 380 * 381 * @see #setLabelYOffset(double) 382 */ 383 public double getLabelYOffset() { 384 return this.labelYOffset; 385 } 386 387 /** 388 * Sets the y-offset and sends a property change event (with the name 389 * 'labelYOffset') to all registered listeners. 390 * 391 * @param offset the new offset. 392 * 393 * @see #getLabelYOffset() 394 */ 395 public void setLabelYOffset(double offset) { 396 Double old = this.labelYOffset; 397 this.labelYOffset = offset; 398 this.pcs.firePropertyChange("labelYOffset", old, offset); 399 } 400 401 /** 402 * Returns the label font. 403 * 404 * @return The label font (never {@code null}). 405 * 406 * @see #setLabelFont(java.awt.Font) 407 */ 408 public Font getLabelFont() { 409 return this.labelFont; 410 } 411 412 /** 413 * Sets the label font and sends a property change event (with the name 414 * 'labelFont') to all registered listeners. 415 * 416 * @param font the font ({@code null} not permitted). 417 * 418 * @see #getLabelFont() 419 */ 420 public void setLabelFont(Font font) { 421 Args.nullNotPermitted(font, "font"); 422 Font old = this.labelFont; 423 this.labelFont = font; 424 this.pcs.firePropertyChange("labelFont", old, font); 425 } 426 427 /** 428 * Returns the label paint. The default value is {@code Color.BLACK}. 429 * 430 * @return The label paint (never {@code null}). 431 * 432 * @see #setLabelPaint 433 */ 434 public Paint getLabelPaint() { 435 return this.labelPaint; 436 } 437 438 /** 439 * Sets the label paint and sends a property change event (with the name 440 * 'labelPaint') to all registered listeners. 441 * 442 * @param paint the paint ({@code null} not permitted). 443 * 444 * @see #getLabelPaint() 445 */ 446 public void setLabelPaint(Paint paint) { 447 Args.nullNotPermitted(paint, "paint"); 448 Paint old = this.labelPaint; 449 this.labelPaint = paint; 450 this.pcs.firePropertyChange("labelPaint", old, paint); 451 } 452 453 /** 454 * Returns the label background paint. 455 * 456 * @return The label background paint (possibly {@code null}). 457 * 458 * @see #setLabelBackgroundPaint(java.awt.Paint) 459 */ 460 public Paint getLabelBackgroundPaint() { 461 return this.labelBackgroundPaint; 462 } 463 464 /** 465 * Sets the label background paint and sends a property change event with 466 * the name 'labelBackgroundPaint') to all registered listeners. 467 * 468 * @param paint the paint ({@code null} permitted). 469 * 470 * @see #getLabelBackgroundPaint() 471 */ 472 public void setLabelBackgroundPaint(Paint paint) { 473 Paint old = this.labelBackgroundPaint; 474 this.labelBackgroundPaint = paint; 475 this.pcs.firePropertyChange("labelBackgroundPaint", old, paint); 476 } 477 478 /** 479 * Returns the flag that controls the visibility of the label outline. 480 * The default value is {@code true}. 481 * 482 * @return A boolean. 483 * 484 * @see #setLabelOutlineVisible(boolean) 485 */ 486 public boolean isLabelOutlineVisible() { 487 return this.labelOutlineVisible; 488 } 489 490 /** 491 * Sets the flag that controls the visibility of the label outlines and 492 * sends a property change event (with the name "labelOutlineVisible") to 493 * all registered listeners. 494 * 495 * @param visible the new flag value. 496 * 497 * @see #isLabelOutlineVisible() 498 */ 499 public void setLabelOutlineVisible(boolean visible) { 500 boolean old = this.labelOutlineVisible; 501 this.labelOutlineVisible = visible; 502 this.pcs.firePropertyChange("labelOutlineVisible", old, visible); 503 } 504 505 /** 506 * Returns the label outline paint. 507 * 508 * @return The label outline paint (never {@code null}). 509 * 510 * @see #setLabelOutlinePaint(java.awt.Paint) 511 */ 512 public Paint getLabelOutlinePaint() { 513 return this.labelOutlinePaint; 514 } 515 516 /** 517 * Sets the label outline paint and sends a property change event (with the 518 * name "labelOutlinePaint") to all registered listeners. 519 * 520 * @param paint the paint ({@code null} not permitted). 521 * 522 * @see #getLabelOutlinePaint() 523 */ 524 public void setLabelOutlinePaint(Paint paint) { 525 Args.nullNotPermitted(paint, "paint"); 526 Paint old = this.labelOutlinePaint; 527 this.labelOutlinePaint = paint; 528 this.pcs.firePropertyChange("labelOutlinePaint", old, paint); 529 } 530 531 /** 532 * Returns the label outline stroke. The default value is 533 * {@code BasicStroke(0.5)}. 534 * 535 * @return The label outline stroke (never {@code null}). 536 * 537 * @see #setLabelOutlineStroke(java.awt.Stroke) 538 */ 539 public Stroke getLabelOutlineStroke() { 540 return this.labelOutlineStroke; 541 } 542 543 /** 544 * Sets the label outline stroke and sends a property change event (with 545 * the name 'labelOutlineStroke') to all registered listeners. 546 * 547 * @param stroke the stroke ({@code null} not permitted). 548 * 549 * @see #getLabelOutlineStroke() 550 */ 551 public void setLabelOutlineStroke(Stroke stroke) { 552 Args.nullNotPermitted(stroke, "stroke"); 553 Stroke old = this.labelOutlineStroke; 554 this.labelOutlineStroke = stroke; 555 this.pcs.firePropertyChange("labelOutlineStroke", old, stroke); 556 } 557 558 /** 559 * Tests this crosshair for equality with an arbitrary object. 560 * 561 * @param obj the object ({@code null} permitted). 562 * 563 * @return A boolean. 564 */ 565 @Override 566 public boolean equals(Object obj) { 567 if (obj == this) { 568 return true; 569 } 570 if (!(obj instanceof Crosshair)) { 571 return false; 572 } 573 Crosshair that = (Crosshair) obj; 574 if (this.visible != that.visible) { 575 return false; 576 } 577 if (this.value != that.value) { 578 return false; 579 } 580 if (!PaintUtils.equal(this.paint, that.paint)) { 581 return false; 582 } 583 if (!this.stroke.equals(that.stroke)) { 584 return false; 585 } 586 if (this.labelVisible != that.labelVisible) { 587 return false; 588 } 589 if (!this.labelGenerator.equals(that.labelGenerator)) { 590 return false; 591 } 592 if (!this.labelAnchor.equals(that.labelAnchor)) { 593 return false; 594 } 595 if (this.labelXOffset != that.labelXOffset) { 596 return false; 597 } 598 if (this.labelYOffset != that.labelYOffset) { 599 return false; 600 } 601 if (!this.labelFont.equals(that.labelFont)) { 602 return false; 603 } 604 if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { 605 return false; 606 } 607 if (!PaintUtils.equal(this.labelBackgroundPaint, 608 that.labelBackgroundPaint)) { 609 return false; 610 } 611 if (this.labelOutlineVisible != that.labelOutlineVisible) { 612 return false; 613 } 614 if (!PaintUtils.equal(this.labelOutlinePaint, 615 that.labelOutlinePaint)) { 616 return false; 617 } 618 if (!this.labelOutlineStroke.equals(that.labelOutlineStroke)) { 619 return false; 620 } 621 return true; // can't find any difference 622 } 623 624 /** 625 * Returns a hash code for this instance. 626 * 627 * @return A hash code. 628 */ 629 @Override 630 public int hashCode() { 631 int hash = 7; 632 hash = HashUtils.hashCode(hash, this.visible); 633 hash = HashUtils.hashCode(hash, this.value); 634 hash = HashUtils.hashCode(hash, this.paint); 635 hash = HashUtils.hashCode(hash, this.stroke); 636 hash = HashUtils.hashCode(hash, this.labelVisible); 637 hash = HashUtils.hashCode(hash, this.labelAnchor); 638 hash = HashUtils.hashCode(hash, this.labelGenerator); 639 hash = HashUtils.hashCode(hash, this.labelXOffset); 640 hash = HashUtils.hashCode(hash, this.labelYOffset); 641 hash = HashUtils.hashCode(hash, this.labelFont); 642 hash = HashUtils.hashCode(hash, this.labelPaint); 643 hash = HashUtils.hashCode(hash, this.labelBackgroundPaint); 644 hash = HashUtils.hashCode(hash, this.labelOutlineVisible); 645 hash = HashUtils.hashCode(hash, this.labelOutlineStroke); 646 hash = HashUtils.hashCode(hash, this.labelOutlinePaint); 647 return hash; 648 } 649 650 /** 651 * Returns an independent copy of this instance. 652 * 653 * @return An independent copy of this instance. 654 * 655 * @throws java.lang.CloneNotSupportedException if there is a problem with 656 * cloning. 657 */ 658 @Override 659 public Object clone() throws CloneNotSupportedException { 660 // FIXME: clone generator 661 return super.clone(); 662 } 663 664 /** 665 * Adds a property change listener. 666 * 667 * @param l the listener. 668 * 669 * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) 670 */ 671 public void addPropertyChangeListener(PropertyChangeListener l) { 672 this.pcs.addPropertyChangeListener(l); 673 } 674 675 /** 676 * Removes a property change listener. 677 * 678 * @param l the listener. 679 * 680 * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) 681 */ 682 public void removePropertyChangeListener(PropertyChangeListener l) { 683 this.pcs.removePropertyChangeListener(l); 684 } 685 686 /** 687 * Provides serialization support. 688 * 689 * @param stream the output stream. 690 * 691 * @throws IOException if there is an I/O error. 692 */ 693 private void writeObject(ObjectOutputStream stream) throws IOException { 694 stream.defaultWriteObject(); 695 SerialUtils.writePaint(this.paint, stream); 696 SerialUtils.writeStroke(this.stroke, stream); 697 SerialUtils.writePaint(this.labelPaint, stream); 698 SerialUtils.writePaint(this.labelBackgroundPaint, stream); 699 SerialUtils.writeStroke(this.labelOutlineStroke, stream); 700 SerialUtils.writePaint(this.labelOutlinePaint, stream); 701 } 702 703 /** 704 * Provides serialization support. 705 * 706 * @param stream the input stream. 707 * 708 * @throws IOException if there is an I/O error. 709 * @throws ClassNotFoundException if there is a classpath problem. 710 */ 711 private void readObject(ObjectInputStream stream) 712 throws IOException, ClassNotFoundException { 713 stream.defaultReadObject(); 714 this.paint = SerialUtils.readPaint(stream); 715 this.stroke = SerialUtils.readStroke(stream); 716 this.labelPaint = SerialUtils.readPaint(stream); 717 this.labelBackgroundPaint = SerialUtils.readPaint(stream); 718 this.labelOutlineStroke = SerialUtils.readStroke(stream); 719 this.labelOutlinePaint = SerialUtils.readPaint(stream); 720 this.pcs = new PropertyChangeSupport(this); 721 } 722 723}