001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2009, 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 * 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 public boolean isVisible() { 187 return this.visible; 188 } 189 190 /** 191 * Sets the flag that controls the visibility of the crosshair and sends 192 * a proerty change event (with the name 'visible') to all registered 193 * listeners. 194 * 195 * @param visible the new flag value. 196 */ 197 public void setVisible(boolean visible) { 198 boolean old = this.visible; 199 this.visible = visible; 200 this.pcs.firePropertyChange("visible", old, visible); 201 } 202 203 /** 204 * Returns the crosshair value. 205 * 206 * @return The crosshair value. 207 */ 208 public double getValue() { 209 return this.value; 210 } 211 212 /** 213 * Sets the crosshair value and sends a property change event with the name 214 * 'value' to all registered listeners. 215 * 216 * @param value the value. 217 */ 218 public void setValue(double value) { 219 Double oldValue = new Double(this.value); 220 this.value = value; 221 this.pcs.firePropertyChange("value", oldValue, new Double(value)); 222 } 223 224 /** 225 * Returns the paint for the crosshair line. 226 * 227 * @return The paint (never <code>null</code>). 228 */ 229 public Paint getPaint() { 230 return this.paint; 231 } 232 233 /** 234 * Sets the paint for the crosshair line and sends a property change event 235 * with the name "paint" to all registered listeners. 236 * 237 * @param paint the paint (<code>null</code> not permitted). 238 */ 239 public void setPaint(Paint paint) { 240 Paint old = this.paint; 241 this.paint = paint; 242 this.pcs.firePropertyChange("paint", old, paint); 243 } 244 245 /** 246 * Returns the stroke for the crosshair line. 247 * 248 * @return The stroke (never <code>null</code>). 249 */ 250 public Stroke getStroke() { 251 return this.stroke; 252 } 253 254 /** 255 * Sets the stroke for the crosshair line and sends a property change event 256 * with the name "stroke" to all registered listeners. 257 * 258 * @param stroke the stroke (<code>null</code> not permitted). 259 */ 260 public void setStroke(Stroke stroke) { 261 Stroke old = this.stroke; 262 this.stroke = stroke; 263 this.pcs.firePropertyChange("stroke", old, stroke); 264 } 265 266 /** 267 * Returns the flag that controls whether or not a label is drawn for 268 * this crosshair. 269 * 270 * @return A boolean. 271 */ 272 public boolean isLabelVisible() { 273 return this.labelVisible; 274 } 275 276 /** 277 * Sets the flag that controls whether or not a label is drawn for the 278 * crosshair and sends a property change event (with the name 279 * 'labelVisible') to all registered listeners. 280 * 281 * @param visible the new flag value. 282 */ 283 public void setLabelVisible(boolean visible) { 284 boolean old = this.labelVisible; 285 this.labelVisible = visible; 286 this.pcs.firePropertyChange("labelVisible", old, visible); 287 } 288 289 /** 290 * Returns the crosshair label generator. 291 * 292 * @return The label crosshair generator (never <code>null</code>). 293 */ 294 public CrosshairLabelGenerator getLabelGenerator() { 295 return this.labelGenerator; 296 } 297 298 /** 299 * Sets the crosshair label generator and sends a property change event 300 * (with the name 'labelGenerator') to all registered listeners. 301 * 302 * @param generator the new generator (<code>null</code> not permitted). 303 */ 304 public void setLabelGenerator(CrosshairLabelGenerator generator) { 305 if (generator == null) { 306 throw new IllegalArgumentException("Null 'generator' argument."); 307 } 308 CrosshairLabelGenerator old = this.labelGenerator; 309 this.labelGenerator = generator; 310 this.pcs.firePropertyChange("labelGenerator", old, generator); 311 } 312 313 /** 314 * Returns the label anchor point. 315 * 316 * @return the label anchor point (never <code>null</code>. 317 */ 318 public RectangleAnchor getLabelAnchor() { 319 return this.labelAnchor; 320 } 321 322 /** 323 * Sets the label anchor point and sends a property change event (with the 324 * name 'labelAnchor') to all registered listeners. 325 * 326 * @param anchor the anchor (<code>null</code> not permitted). 327 */ 328 public void setLabelAnchor(RectangleAnchor anchor) { 329 RectangleAnchor old = this.labelAnchor; 330 this.labelAnchor = anchor; 331 this.pcs.firePropertyChange("labelAnchor", old, anchor); 332 } 333 334 /** 335 * Returns the x-offset for the label (in Java2D units). 336 * 337 * @return The x-offset. 338 */ 339 public double getLabelXOffset() { 340 return this.labelXOffset; 341 } 342 343 /** 344 * Sets the x-offset and sends a property change event (with the name 345 * 'labelXOffset') to all registered listeners. 346 * 347 * @param offset the new offset. 348 */ 349 public void setLabelXOffset(double offset) { 350 Double old = new Double(this.labelXOffset); 351 this.labelXOffset = offset; 352 this.pcs.firePropertyChange("labelXOffset", old, new Double(offset)); 353 } 354 355 /** 356 * Returns the y-offset for the label (in Java2D units). 357 * 358 * @return The y-offset. 359 */ 360 public double getLabelYOffset() { 361 return this.labelYOffset; 362 } 363 364 /** 365 * Sets the y-offset and sends a property change event (with the name 366 * 'labelYOffset') to all registered listeners. 367 * 368 * @param offset the new offset. 369 */ 370 public void setLabelYOffset(double offset) { 371 Double old = new Double(this.labelYOffset); 372 this.labelYOffset = offset; 373 this.pcs.firePropertyChange("labelYOffset", old, new Double(offset)); 374 } 375 376 /** 377 * Returns the label font. 378 * 379 * @return The label font (never <code>null</code>). 380 */ 381 public Font getLabelFont() { 382 return this.labelFont; 383 } 384 385 /** 386 * Sets the label font and sends a property change event (with the name 387 * 'labelFont') to all registered listeners. 388 * 389 * @param font the font (<code>null</code> not permitted). 390 */ 391 public void setLabelFont(Font font) { 392 if (font == null) { 393 throw new IllegalArgumentException("Null 'font' argument."); 394 } 395 Font old = this.labelFont; 396 this.labelFont = font; 397 this.pcs.firePropertyChange("labelFont", old, font); 398 } 399 400 /** 401 * Returns the label paint. 402 * 403 * @return The label paint (never <code>null</code>). 404 */ 405 public Paint getLabelPaint() { 406 return this.labelPaint; 407 } 408 409 /** 410 * Sets the label paint and sends a property change event (with the name 411 * 'labelPaint') to all registered listeners. 412 * 413 * @param paint the paint (<code>null</code> not permitted). 414 */ 415 public void setLabelPaint(Paint paint) { 416 if (paint == null) { 417 throw new IllegalArgumentException("Null 'paint' argument."); 418 } 419 Paint old = this.labelPaint; 420 this.labelPaint = paint; 421 this.pcs.firePropertyChange("labelPaint", old, paint); 422 } 423 424 /** 425 * Returns the label background paint. 426 * 427 * @return The label background paint (possibly <code>null</code>). 428 */ 429 public Paint getLabelBackgroundPaint() { 430 return this.labelBackgroundPaint; 431 } 432 433 /** 434 * Sets the label background paint and sends a property change event with 435 * the name 'labelBackgroundPaint') to all registered listeners. 436 * 437 * @param paint the paint (<code>null</code> permitted). 438 */ 439 public void setLabelBackgroundPaint(Paint paint) { 440 Paint old = this.labelBackgroundPaint; 441 this.labelBackgroundPaint = paint; 442 this.pcs.firePropertyChange("labelBackgroundPaint", old, paint); 443 } 444 445 /** 446 * Returns the flag that controls the visibility of the label outline. 447 * 448 * @return A boolean. 449 */ 450 public boolean isLabelOutlineVisible() { 451 return this.labelOutlineVisible; 452 } 453 454 /** 455 * Sets the flag that controls the visibility of the label outlines and 456 * sends a property change event (with the name "labelOutlineVisible") to 457 * all registered listeners. 458 * 459 * @param visible the new flag value. 460 */ 461 public void setLabelOutlineVisible(boolean visible) { 462 boolean old = this.labelOutlineVisible; 463 this.labelOutlineVisible = visible; 464 this.pcs.firePropertyChange("labelOutlineVisible", old, visible); 465 } 466 467 /** 468 * Returns the label outline paint. 469 * 470 * @return The label outline paint (never <code>null</code>). 471 */ 472 public Paint getLabelOutlinePaint() { 473 return this.labelOutlinePaint; 474 } 475 476 /** 477 * Sets the label outline paint and sends a property change event (with the 478 * name "labelOutlinePaint") to all registered listeners. 479 * 480 * @param paint the paint (<code>null</code> not permitted). 481 */ 482 public void setLabelOutlinePaint(Paint paint) { 483 if (paint == null) { 484 throw new IllegalArgumentException("Null 'paint' argument."); 485 } 486 Paint old = this.labelOutlinePaint; 487 this.labelOutlinePaint = paint; 488 this.pcs.firePropertyChange("labelOutlinePaint", old, paint); 489 } 490 491 /** 492 * Returns the label outline stroke. 493 * 494 * @return The label outline stroke (never <code>null</code>). 495 */ 496 public Stroke getLabelOutlineStroke() { 497 return this.labelOutlineStroke; 498 } 499 500 /** 501 * Sets the label outline stroke and sends a property change event (with 502 * the name 'labelOutlineStroke') to all registered listeners. 503 * 504 * @param stroke the stroke (<code>null</code> not permitted). 505 */ 506 public void setLabelOutlineStroke(Stroke stroke) { 507 if (stroke == null) { 508 throw new IllegalArgumentException("Null 'stroke' argument."); 509 } 510 Stroke old = this.labelOutlineStroke; 511 this.labelOutlineStroke = stroke; 512 this.pcs.firePropertyChange("labelOutlineStroke", old, stroke); 513 } 514 515 /** 516 * Tests this crosshair for equality with an arbitrary object. 517 * 518 * @param obj the object (<code>null</code> permitted). 519 * 520 * @return A boolean. 521 */ 522 public boolean equals(Object obj) { 523 if (obj == this) { 524 return true; 525 } 526 if (!(obj instanceof Crosshair)) { 527 return false; 528 } 529 Crosshair that = (Crosshair) obj; 530 if (this.visible != that.visible) { 531 return false; 532 } 533 if (this.value != that.value) { 534 return false; 535 } 536 if (!PaintUtilities.equal(this.paint, that.paint)) { 537 return false; 538 } 539 if (!this.stroke.equals(that.stroke)) { 540 return false; 541 } 542 if (this.labelVisible != that.labelVisible) { 543 return false; 544 } 545 if (!this.labelGenerator.equals(that.labelGenerator)) { 546 return false; 547 } 548 if (!this.labelAnchor.equals(that.labelAnchor)) { 549 return false; 550 } 551 if (this.labelXOffset != that.labelXOffset) { 552 return false; 553 } 554 if (this.labelYOffset != that.labelYOffset) { 555 return false; 556 } 557 if (!this.labelFont.equals(that.labelFont)) { 558 return false; 559 } 560 if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) { 561 return false; 562 } 563 if (!PaintUtilities.equal(this.labelBackgroundPaint, 564 that.labelBackgroundPaint)) { 565 return false; 566 } 567 if (this.labelOutlineVisible != that.labelOutlineVisible) { 568 return false; 569 } 570 if (!PaintUtilities.equal(this.labelOutlinePaint, 571 that.labelOutlinePaint)) { 572 return false; 573 } 574 if (!this.labelOutlineStroke.equals(that.labelOutlineStroke)) { 575 return false; 576 } 577 return true; // can't find any difference 578 } 579 580 /** 581 * Returns a hash code for this instance. 582 * 583 * @return A hash code. 584 */ 585 public int hashCode() { 586 int hash = 7; 587 hash = HashUtilities.hashCode(hash, this.visible); 588 hash = HashUtilities.hashCode(hash, this.value); 589 hash = HashUtilities.hashCode(hash, this.paint); 590 hash = HashUtilities.hashCode(hash, this.stroke); 591 hash = HashUtilities.hashCode(hash, this.labelVisible); 592 hash = HashUtilities.hashCode(hash, this.labelAnchor); 593 hash = HashUtilities.hashCode(hash, this.labelGenerator); 594 hash = HashUtilities.hashCode(hash, this.labelXOffset); 595 hash = HashUtilities.hashCode(hash, this.labelYOffset); 596 hash = HashUtilities.hashCode(hash, this.labelFont); 597 hash = HashUtilities.hashCode(hash, this.labelPaint); 598 hash = HashUtilities.hashCode(hash, this.labelBackgroundPaint); 599 hash = HashUtilities.hashCode(hash, this.labelOutlineVisible); 600 hash = HashUtilities.hashCode(hash, this.labelOutlineStroke); 601 hash = HashUtilities.hashCode(hash, this.labelOutlinePaint); 602 return hash; 603 } 604 605 /** 606 * Returns an independent copy of this instance. 607 * 608 * @return An independent copy of this instance. 609 * 610 * @throws java.lang.CloneNotSupportedException 611 */ 612 public Object clone() throws CloneNotSupportedException { 613 // FIXME: clone generator 614 return super.clone(); 615 } 616 617 /** 618 * Adds a property change listener. 619 * 620 * @param l the listener. 621 */ 622 public void addPropertyChangeListener(PropertyChangeListener l) { 623 this.pcs.addPropertyChangeListener(l); 624 } 625 626 /** 627 * Removes a property change listener. 628 * 629 * @param l the listener. 630 */ 631 public void removePropertyChangeListener(PropertyChangeListener l) { 632 this.pcs.removePropertyChangeListener(l); 633 } 634 635 /** 636 * Provides serialization support. 637 * 638 * @param stream the output stream. 639 * 640 * @throws IOException if there is an I/O error. 641 */ 642 private void writeObject(ObjectOutputStream stream) throws IOException { 643 stream.defaultWriteObject(); 644 SerialUtilities.writePaint(this.paint, stream); 645 SerialUtilities.writeStroke(this.stroke, stream); 646 SerialUtilities.writePaint(this.labelPaint, stream); 647 SerialUtilities.writePaint(this.labelBackgroundPaint, stream); 648 SerialUtilities.writeStroke(this.labelOutlineStroke, stream); 649 SerialUtilities.writePaint(this.labelOutlinePaint, stream); 650 } 651 652 /** 653 * Provides serialization support. 654 * 655 * @param stream the input stream. 656 * 657 * @throws IOException if there is an I/O error. 658 * @throws ClassNotFoundException if there is a classpath problem. 659 */ 660 private void readObject(ObjectInputStream stream) 661 throws IOException, ClassNotFoundException { 662 stream.defaultReadObject(); 663 this.paint = SerialUtilities.readPaint(stream); 664 this.stroke = SerialUtilities.readStroke(stream); 665 this.labelPaint = SerialUtilities.readPaint(stream); 666 this.labelBackgroundPaint = SerialUtilities.readPaint(stream); 667 this.labelOutlineStroke = SerialUtilities.readStroke(stream); 668 this.labelOutlinePaint = SerialUtilities.readPaint(stream); 669 this.pcs = new PropertyChangeSupport(this); 670 } 671 672 }