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 }