/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2014, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* --------------
* Crosshair.java
* --------------
* (C) Copyright 2009-2014, by Object Refinery Limited.
*
* Original Author: David Gilbert (for Object Refinery Limited);
* Contributor(s): -;
*
* Changes:
* --------
* 13-Feb-2009 : Version 1 (DG);
* 17-Jun-2012 : Removed JCommon dependencies (DG);
*
*/
package org.jfree.chart.plot;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Stroke;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.jfree.chart.util.HashUtils;
import org.jfree.chart.ui.RectangleAnchor;
import org.jfree.chart.util.PaintUtils;
import org.jfree.chart.util.PublicCloneable;
import org.jfree.chart.labels.CrosshairLabelGenerator;
import org.jfree.chart.labels.StandardCrosshairLabelGenerator;
import org.jfree.chart.util.SerialUtils;
/**
* A crosshair for display on a plot.
*
* @since 1.0.13
*/
public class Crosshair implements Cloneable, PublicCloneable, Serializable {
/** Flag controlling visibility. */
private boolean visible;
/** The crosshair value. */
private double value;
/** The paint for the crosshair line. */
private transient Paint paint;
/** The stroke for the crosshair line. */
private transient Stroke stroke;
/**
* A flag that controls whether or not the crosshair has a label
* visible.
*/
private boolean labelVisible;
/**
* The label anchor.
*/
private RectangleAnchor labelAnchor;
/** A label generator. */
private CrosshairLabelGenerator labelGenerator;
/**
* The x-offset in Java2D units.
*/
private double labelXOffset;
/**
* The y-offset in Java2D units.
*/
private double labelYOffset;
/**
* The label font.
*/
private Font labelFont;
/**
* The label paint.
*/
private transient Paint labelPaint;
/**
* The label background paint.
*/
private transient Paint labelBackgroundPaint;
/** A flag that controls the visibility of the label outline. */
private boolean labelOutlineVisible;
/** The label outline stroke. */
private transient Stroke labelOutlineStroke;
/** The label outline paint. */
private transient Paint labelOutlinePaint;
/** Property change support. */
private transient PropertyChangeSupport pcs;
/**
* Creates a new crosshair with value 0.0.
*/
public Crosshair() {
this(0.0);
}
/**
* Creates a new crosshair with the specified value.
*
* @param value the value.
*/
public Crosshair(double value) {
this(value, Color.BLACK, new BasicStroke(1.0f));
}
/**
* Creates a new crosshair value with the specified value and line style.
*
* @param value the value.
* @param paint the line paint (<code>null</code> not permitted).
* @param stroke the line stroke (<code>null</code> not permitted).
*/
public Crosshair(double value, Paint paint, Stroke stroke) {
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
this.visible = true;
this.value = value;
this.paint = paint;
this.stroke = stroke;
this.labelVisible = false;
this.labelGenerator = new StandardCrosshairLabelGenerator();
this.labelAnchor = RectangleAnchor.BOTTOM_LEFT;
this.labelXOffset = 3.0;
this.labelYOffset = 3.0;
this.labelFont = new Font("Tahoma", Font.PLAIN, 12);
this.labelPaint = Color.BLACK;
this.labelBackgroundPaint = new Color(0, 0, 255, 63);
this.labelOutlineVisible = true;
this.labelOutlinePaint = Color.BLACK;
this.labelOutlineStroke = new BasicStroke(0.5f);
this.pcs = new PropertyChangeSupport(this);
}
/**
* Returns the flag that indicates whether or not the crosshair is
* currently visible.
*
* @return A boolean.
*
* @see #setVisible(boolean)
*/
public boolean isVisible() {
return this.visible;
}
/**
* Sets the flag that controls the visibility of the crosshair and sends
* a proerty change event (with the name 'visible') to all registered
* listeners.
*
* @param visible the new flag value.
*
* @see #isVisible()
*/
public void setVisible(boolean visible) {
boolean old = this.visible;
this.visible = visible;
this.pcs.firePropertyChange("visible", old, visible);
}
/**
* Returns the crosshair value.
*
* @return The crosshair value.
*
* @see #setValue(double)
*/
public double getValue() {
return this.value;
}
/**
* Sets the crosshair value and sends a property change event with the name
* 'value' to all registered listeners.
*
* @param value the value.
*
* @see #getValue()
*/
public void setValue(double value) {
Double oldValue = this.value;
this.value = value;
this.pcs.firePropertyChange("value", oldValue, value);
}
/**
* Returns the paint for the crosshair line.
*
* @return The paint (never <code>null</code>).
*
* @see #setPaint(java.awt.Paint)
*/
public Paint getPaint() {
return this.paint;
}
/**
* Sets the paint for the crosshair line and sends a property change event
* with the name "paint" to all registered listeners.
*
* @param paint the paint (<code>null</code> not permitted).
*
* @see #getPaint()
*/
public void setPaint(Paint paint) {
Paint old = this.paint;
this.paint = paint;
this.pcs.firePropertyChange("paint", old, paint);
}
/**
* Returns the stroke for the crosshair line.
*
* @return The stroke (never <code>null</code>).
*
* @see #setStroke(java.awt.Stroke)
*/
public Stroke getStroke() {
return this.stroke;
}
/**
* Sets the stroke for the crosshair line and sends a property change event
* with the name "stroke" to all registered listeners.
*
* @param stroke the stroke (<code>null</code> not permitted).
*
* @see #getStroke()
*/
public void setStroke(Stroke stroke) {
Stroke old = this.stroke;
this.stroke = stroke;
this.pcs.firePropertyChange("stroke", old, stroke);
}
/**
* Returns the flag that controls whether or not a label is drawn for
* this crosshair.
*
* @return A boolean.
*
* @see #setLabelVisible(boolean)
*/
public boolean isLabelVisible() {
return this.labelVisible;
}
/**
* Sets the flag that controls whether or not a label is drawn for the
* crosshair and sends a property change event (with the name
* 'labelVisible') to all registered listeners.
*
* @param visible the new flag value.
*
* @see #isLabelVisible()
*/
public void setLabelVisible(boolean visible) {
boolean old = this.labelVisible;
this.labelVisible = visible;
this.pcs.firePropertyChange("labelVisible", old, visible);
}
/**
* Returns the crosshair label generator.
*
* @return The label crosshair generator (never <code>null</code>).
*
* @see #setLabelGenerator(org.jfree.chart.labels.CrosshairLabelGenerator)
*/
public CrosshairLabelGenerator getLabelGenerator() {
return this.labelGenerator;
}
/**
* Sets the crosshair label generator and sends a property change event
* (with the name 'labelGenerator') to all registered listeners.
*
* @param generator the new generator (<code>null</code> not permitted).
*
* @see #getLabelGenerator()
*/
public void setLabelGenerator(CrosshairLabelGenerator generator) {
if (generator == null) {
throw new IllegalArgumentException("Null 'generator' argument.");
}
CrosshairLabelGenerator old = this.labelGenerator;
this.labelGenerator = generator;
this.pcs.firePropertyChange("labelGenerator", old, generator);
}
/**
* Returns the label anchor point.
*
* @return the label anchor point (never <code>null</code>.
*
* @see #setLabelAnchor(org.jfree.chart.ui.RectangleAnchor)
*/
public RectangleAnchor getLabelAnchor() {
return this.labelAnchor;
}
/**
* Sets the label anchor point and sends a property change event (with the
* name 'labelAnchor') to all registered listeners.
*
* @param anchor the anchor (<code>null</code> not permitted).
*
* @see #getLabelAnchor()
*/
public void setLabelAnchor(RectangleAnchor anchor) {
RectangleAnchor old = this.labelAnchor;
this.labelAnchor = anchor;
this.pcs.firePropertyChange("labelAnchor", old, anchor);
}
/**
* Returns the x-offset for the label (in Java2D units).
*
* @return The x-offset.
*
* @see #setLabelXOffset(double)
*/
public double getLabelXOffset() {
return this.labelXOffset;
}
/**
* Sets the x-offset and sends a property change event (with the name
* 'labelXOffset') to all registered listeners.
*
* @param offset the new offset.
*
* @see #getLabelXOffset()
*/
public void setLabelXOffset(double offset) {
Double old = this.labelXOffset;
this.labelXOffset = offset;
this.pcs.firePropertyChange("labelXOffset", old, offset);
}
/**
* Returns the y-offset for the label (in Java2D units).
*
* @return The y-offset.
*
* @see #setLabelYOffset(double)
*/
public double getLabelYOffset() {
return this.labelYOffset;
}
/**
* Sets the y-offset and sends a property change event (with the name
* 'labelYOffset') to all registered listeners.
*
* @param offset the new offset.
*
* @see #getLabelYOffset()
*/
public void setLabelYOffset(double offset) {
Double old = this.labelYOffset;
this.labelYOffset = offset;
this.pcs.firePropertyChange("labelYOffset", old, offset);
}
/**
* Returns the label font.
*
* @return The label font (never <code>null</code>).
*
* @see #setLabelFont(java.awt.Font)
*/
public Font getLabelFont() {
return this.labelFont;
}
/**
* Sets the label font and sends a property change event (with the name
* 'labelFont') to all registered listeners.
*
* @param font the font (<code>null</code> not permitted).
*
* @see #getLabelFont()
*/
public void setLabelFont(Font font) {
if (font == null) {
throw new IllegalArgumentException("Null 'font' argument.");
}
Font old = this.labelFont;
this.labelFont = font;
this.pcs.firePropertyChange("labelFont", old, font);
}
/**
* Returns the label paint.
*
* @return The label paint (never <code>null</code>).
*
* @see #setLabelPaint
*/
public Paint getLabelPaint() {
return this.labelPaint;
}
/**
* Sets the label paint and sends a property change event (with the name
* 'labelPaint') to all registered listeners.
*
* @param paint the paint (<code>null</code> not permitted).
*
* @see #getLabelPaint()
*/
public void setLabelPaint(Paint paint) {
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
Paint old = this.labelPaint;
this.labelPaint = paint;
this.pcs.firePropertyChange("labelPaint", old, paint);
}
/**
* Returns the label background paint.
*
* @return The label background paint (possibly <code>null</code>).
*
* @see #setLabelBackgroundPaint(java.awt.Paint)
*/
public Paint getLabelBackgroundPaint() {
return this.labelBackgroundPaint;
}
/**
* Sets the label background paint and sends a property change event with
* the name 'labelBackgroundPaint') to all registered listeners.
*
* @param paint the paint (<code>null</code> permitted).
*
* @see #getLabelBackgroundPaint()
*/
public void setLabelBackgroundPaint(Paint paint) {
Paint old = this.labelBackgroundPaint;
this.labelBackgroundPaint = paint;
this.pcs.firePropertyChange("labelBackgroundPaint", old, paint);
}
/**
* Returns the flag that controls the visibility of the label outline.
*
* @return A boolean.
*
* @see #setLabelOutlineVisible(boolean)
*/
public boolean isLabelOutlineVisible() {
return this.labelOutlineVisible;
}
/**
* Sets the flag that controls the visibility of the label outlines and
* sends a property change event (with the name "labelOutlineVisible") to
* all registered listeners.
*
* @param visible the new flag value.
*
* @see #isLabelOutlineVisible()
*/
public void setLabelOutlineVisible(boolean visible) {
boolean old = this.labelOutlineVisible;
this.labelOutlineVisible = visible;
this.pcs.firePropertyChange("labelOutlineVisible", old, visible);
}
/**
* Returns the label outline paint.
*
* @return The label outline paint (never <code>null</code>).
*
* @see #setLabelOutlinePaint(java.awt.Paint)
*/
public Paint getLabelOutlinePaint() {
return this.labelOutlinePaint;
}
/**
* Sets the label outline paint and sends a property change event (with the
* name "labelOutlinePaint") to all registered listeners.
*
* @param paint the paint (<code>null</code> not permitted).
*
* @see #getLabelOutlinePaint()
*/
public void setLabelOutlinePaint(Paint paint) {
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
Paint old = this.labelOutlinePaint;
this.labelOutlinePaint = paint;
this.pcs.firePropertyChange("labelOutlinePaint", old, paint);
}
/**
* Returns the label outline stroke.
*
* @return The label outline stroke (never <code>null</code>).
*
* @see #setLabelOutlineStroke(java.awt.Stroke)
*/
public Stroke getLabelOutlineStroke() {
return this.labelOutlineStroke;
}
/**
* Sets the label outline stroke and sends a property change event (with
* the name 'labelOutlineStroke') to all registered listeners.
*
* @param stroke the stroke (<code>null</code> not permitted).
*
* @see #getLabelOutlineStroke()
*/
public void setLabelOutlineStroke(Stroke stroke) {
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
Stroke old = this.labelOutlineStroke;
this.labelOutlineStroke = stroke;
this.pcs.firePropertyChange("labelOutlineStroke", old, stroke);
}
/**
* Tests this crosshair for equality with an arbitrary object.
*
* @param obj the object (<code>null</code> permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Crosshair)) {
return false;
}
Crosshair that = (Crosshair) obj;
if (this.visible != that.visible) {
return false;
}
if (this.value != that.value) {
return false;
}
if (!PaintUtils.equal(this.paint, that.paint)) {
return false;
}
if (!this.stroke.equals(that.stroke)) {
return false;
}
if (this.labelVisible != that.labelVisible) {
return false;
}
if (!this.labelGenerator.equals(that.labelGenerator)) {
return false;
}
if (!this.labelAnchor.equals(that.labelAnchor)) {
return false;
}
if (this.labelXOffset != that.labelXOffset) {
return false;
}
if (this.labelYOffset != that.labelYOffset) {
return false;
}
if (!this.labelFont.equals(that.labelFont)) {
return false;
}
if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) {
return false;
}
if (!PaintUtils.equal(this.labelBackgroundPaint,
that.labelBackgroundPaint)) {
return false;
}
if (this.labelOutlineVisible != that.labelOutlineVisible) {
return false;
}
if (!PaintUtils.equal(this.labelOutlinePaint,
that.labelOutlinePaint)) {
return false;
}
if (!this.labelOutlineStroke.equals(that.labelOutlineStroke)) {
return false;
}
return true; // can't find any difference
}
/**
* Returns a hash code for this instance.
*
* @return A hash code.
*/
@Override
public int hashCode() {
int hash = 7;
hash = HashUtils.hashCode(hash, this.visible);
hash = HashUtils.hashCode(hash, this.value);
hash = HashUtils.hashCode(hash, this.paint);
hash = HashUtils.hashCode(hash, this.stroke);
hash = HashUtils.hashCode(hash, this.labelVisible);
hash = HashUtils.hashCode(hash, this.labelAnchor);
hash = HashUtils.hashCode(hash, this.labelGenerator);
hash = HashUtils.hashCode(hash, this.labelXOffset);
hash = HashUtils.hashCode(hash, this.labelYOffset);
hash = HashUtils.hashCode(hash, this.labelFont);
hash = HashUtils.hashCode(hash, this.labelPaint);
hash = HashUtils.hashCode(hash, this.labelBackgroundPaint);
hash = HashUtils.hashCode(hash, this.labelOutlineVisible);
hash = HashUtils.hashCode(hash, this.labelOutlineStroke);
hash = HashUtils.hashCode(hash, this.labelOutlinePaint);
return hash;
}
/**
* Returns an independent copy of this instance.
*
* @return An independent copy of this instance.
*
* @throws java.lang.CloneNotSupportedException if there is a problem
* cloning.
*/
@Override
public Object clone() throws CloneNotSupportedException {
// FIXME: clone generator
return super.clone();
}
/**
* Adds a property change listener.
*
* @param l the listener.
*
* @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
this.pcs.addPropertyChangeListener(l);
}
/**
* Removes a property change listener.
*
* @param l the listener.
*
* @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
*/
public void removePropertyChangeListener(PropertyChangeListener l) {
this.pcs.removePropertyChangeListener(l);
}
/**
* Provides serialization support.
*
* @param stream the output stream.
*
* @throws IOException if there is an I/O error.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
SerialUtils.writePaint(this.paint, stream);
SerialUtils.writeStroke(this.stroke, stream);
SerialUtils.writePaint(this.labelPaint, stream);
SerialUtils.writePaint(this.labelBackgroundPaint, stream);
SerialUtils.writeStroke(this.labelOutlineStroke, stream);
SerialUtils.writePaint(this.labelOutlinePaint, stream);
}
/**
* Provides serialization support.
*
* @param stream the input stream.
*
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.paint = SerialUtils.readPaint(stream);
this.stroke = SerialUtils.readStroke(stream);
this.labelPaint = SerialUtils.readPaint(stream);
this.labelBackgroundPaint = SerialUtils.readPaint(stream);
this.labelOutlineStroke = SerialUtils.readStroke(stream);
this.labelOutlinePaint = SerialUtils.readPaint(stream);
this.pcs = new PropertyChangeSupport(this);
}
}