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