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