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 * PaintScaleLegend.java
029 * ---------------------
030 * (C) Copyright 2007-2009, by Object Refinery Limited.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): Peter Kolb - see patch 2686872;
034 *
035 * Changes
036 * -------
037 * 22-Jan-2007 : Version 1 (DG);
038 * 18-Jun-2008 : Fixed bug drawing scale with log axis (DG);
039 * 16-Apr-2009 : Patch 2686872 implementing AxisChangeListener, and fix for
040 * ignored stripOutlineVisible flag (DG);
041 *
042 */
043
044 package org.jfree.chart.title;
045
046 import java.awt.BasicStroke;
047 import java.awt.Color;
048 import java.awt.Graphics2D;
049 import java.awt.Paint;
050 import java.awt.Stroke;
051 import java.awt.geom.Rectangle2D;
052 import java.io.IOException;
053 import java.io.ObjectInputStream;
054 import java.io.ObjectOutputStream;
055
056 import org.jfree.chart.axis.AxisLocation;
057 import org.jfree.chart.axis.AxisSpace;
058 import org.jfree.chart.axis.ValueAxis;
059 import org.jfree.chart.block.LengthConstraintType;
060 import org.jfree.chart.block.RectangleConstraint;
061 import org.jfree.chart.event.AxisChangeEvent;
062 import org.jfree.chart.event.AxisChangeListener;
063 import org.jfree.chart.event.TitleChangeEvent;
064 import org.jfree.chart.plot.Plot;
065 import org.jfree.chart.plot.PlotOrientation;
066 import org.jfree.chart.renderer.PaintScale;
067 import org.jfree.data.Range;
068 import org.jfree.io.SerialUtilities;
069 import org.jfree.ui.RectangleEdge;
070 import org.jfree.ui.Size2D;
071 import org.jfree.util.PaintUtilities;
072 import org.jfree.util.PublicCloneable;
073
074 /**
075 * A legend that shows a range of values and their associated colors, driven
076 * by an underlying {@link PaintScale} implementation.
077 *
078 * @since 1.0.4
079 */
080 public class PaintScaleLegend extends Title implements AxisChangeListener,
081 PublicCloneable {
082
083 /** For serialization. */
084 static final long serialVersionUID = -1365146490993227503L;
085
086 /** The paint scale (never <code>null</code>). */
087 private PaintScale scale;
088
089 /** The value axis (never <code>null</code>). */
090 private ValueAxis axis;
091
092 /**
093 * The axis location (handles both orientations, never
094 * <code>null</code>).
095 */
096 private AxisLocation axisLocation;
097
098 /** The offset between the axis and the paint strip (in Java2D units). */
099 private double axisOffset;
100
101 /** The thickness of the paint strip (in Java2D units). */
102 private double stripWidth;
103
104 /**
105 * A flag that controls whether or not an outline is drawn around the
106 * paint strip.
107 */
108 private boolean stripOutlineVisible;
109
110 /** The paint used to draw an outline around the paint strip. */
111 private transient Paint stripOutlinePaint;
112
113 /** The stroke used to draw an outline around the paint strip. */
114 private transient Stroke stripOutlineStroke;
115
116 /** The background paint (never <code>null</code>). */
117 private transient Paint backgroundPaint;
118
119 /**
120 * The number of subdivisions for the scale when rendering.
121 *
122 * @since 1.0.11
123 */
124 private int subdivisions;
125
126 /**
127 * Creates a new instance.
128 *
129 * @param scale the scale (<code>null</code> not permitted).
130 * @param axis the axis (<code>null</code> not permitted).
131 */
132 public PaintScaleLegend(PaintScale scale, ValueAxis axis) {
133 if (axis == null) {
134 throw new IllegalArgumentException("Null 'axis' argument.");
135 }
136 this.scale = scale;
137 this.axis = axis;
138 this.axis.addChangeListener(this);
139 this.axisLocation = AxisLocation.BOTTOM_OR_LEFT;
140 this.axisOffset = 0.0;
141 this.axis.setRange(scale.getLowerBound(), scale.getUpperBound());
142 this.stripWidth = 15.0;
143 this.stripOutlineVisible = true;
144 this.stripOutlinePaint = Color.gray;
145 this.stripOutlineStroke = new BasicStroke(0.5f);
146 this.backgroundPaint = Color.white;
147 this.subdivisions = 100;
148 }
149
150 /**
151 * Returns the scale used to convert values to colors.
152 *
153 * @return The scale (never <code>null</code>).
154 *
155 * @see #setScale(PaintScale)
156 */
157 public PaintScale getScale() {
158 return this.scale;
159 }
160
161 /**
162 * Sets the scale and sends a {@link TitleChangeEvent} to all registered
163 * listeners.
164 *
165 * @param scale the scale (<code>null</code> not permitted).
166 *
167 * @see #getScale()
168 */
169 public void setScale(PaintScale scale) {
170 if (scale == null) {
171 throw new IllegalArgumentException("Null 'scale' argument.");
172 }
173 this.scale = scale;
174 notifyListeners(new TitleChangeEvent(this));
175 }
176
177 /**
178 * Returns the axis for the paint scale.
179 *
180 * @return The axis (never <code>null</code>).
181 *
182 * @see #setAxis(ValueAxis)
183 */
184 public ValueAxis getAxis() {
185 return this.axis;
186 }
187
188 /**
189 * Sets the axis for the paint scale and sends a {@link TitleChangeEvent}
190 * to all registered listeners.
191 *
192 * @param axis the axis (<code>null</code> not permitted).
193 *
194 * @see #getAxis()
195 */
196 public void setAxis(ValueAxis axis) {
197 if (axis == null) {
198 throw new IllegalArgumentException("Null 'axis' argument.");
199 }
200 this.axis.removeChangeListener(this);
201 this.axis = axis;
202 this.axis.addChangeListener(this);
203 notifyListeners(new TitleChangeEvent(this));
204 }
205
206 /**
207 * Returns the axis location.
208 *
209 * @return The axis location (never <code>null</code>).
210 *
211 * @see #setAxisLocation(AxisLocation)
212 */
213 public AxisLocation getAxisLocation() {
214 return this.axisLocation;
215 }
216
217 /**
218 * Sets the axis location and sends a {@link TitleChangeEvent} to all
219 * registered listeners.
220 *
221 * @param location the location (<code>null</code> not permitted).
222 *
223 * @see #getAxisLocation()
224 */
225 public void setAxisLocation(AxisLocation location) {
226 if (location == null) {
227 throw new IllegalArgumentException("Null 'location' argument.");
228 }
229 this.axisLocation = location;
230 notifyListeners(new TitleChangeEvent(this));
231 }
232
233 /**
234 * Returns the offset between the axis and the paint strip.
235 *
236 * @return The offset between the axis and the paint strip.
237 *
238 * @see #setAxisOffset(double)
239 */
240 public double getAxisOffset() {
241 return this.axisOffset;
242 }
243
244 /**
245 * Sets the offset between the axis and the paint strip and sends a
246 * {@link TitleChangeEvent} to all registered listeners.
247 *
248 * @param offset the offset.
249 */
250 public void setAxisOffset(double offset) {
251 this.axisOffset = offset;
252 notifyListeners(new TitleChangeEvent(this));
253 }
254
255 /**
256 * Returns the width of the paint strip, in Java2D units.
257 *
258 * @return The width of the paint strip.
259 *
260 * @see #setStripWidth(double)
261 */
262 public double getStripWidth() {
263 return this.stripWidth;
264 }
265
266 /**
267 * Sets the width of the paint strip and sends a {@link TitleChangeEvent}
268 * to all registered listeners.
269 *
270 * @param width the width.
271 *
272 * @see #getStripWidth()
273 */
274 public void setStripWidth(double width) {
275 this.stripWidth = width;
276 notifyListeners(new TitleChangeEvent(this));
277 }
278
279 /**
280 * Returns the flag that controls whether or not an outline is drawn
281 * around the paint strip.
282 *
283 * @return A boolean.
284 *
285 * @see #setStripOutlineVisible(boolean)
286 */
287 public boolean isStripOutlineVisible() {
288 return this.stripOutlineVisible;
289 }
290
291 /**
292 * Sets the flag that controls whether or not an outline is drawn around
293 * the paint strip, and sends a {@link TitleChangeEvent} to all registered
294 * listeners.
295 *
296 * @param visible the flag.
297 *
298 * @see #isStripOutlineVisible()
299 */
300 public void setStripOutlineVisible(boolean visible) {
301 this.stripOutlineVisible = visible;
302 notifyListeners(new TitleChangeEvent(this));
303 }
304
305 /**
306 * Returns the paint used to draw the outline of the paint strip.
307 *
308 * @return The paint (never <code>null</code>).
309 *
310 * @see #setStripOutlinePaint(Paint)
311 */
312 public Paint getStripOutlinePaint() {
313 return this.stripOutlinePaint;
314 }
315
316 /**
317 * Sets the paint used to draw the outline of the paint strip, and sends
318 * a {@link TitleChangeEvent} to all registered listeners.
319 *
320 * @param paint the paint (<code>null</code> not permitted).
321 *
322 * @see #getStripOutlinePaint()
323 */
324 public void setStripOutlinePaint(Paint paint) {
325 if (paint == null) {
326 throw new IllegalArgumentException("Null 'paint' argument.");
327 }
328 this.stripOutlinePaint = paint;
329 notifyListeners(new TitleChangeEvent(this));
330 }
331
332 /**
333 * Returns the stroke used to draw the outline around the paint strip.
334 *
335 * @return The stroke (never <code>null</code>).
336 *
337 * @see #setStripOutlineStroke(Stroke)
338 */
339 public Stroke getStripOutlineStroke() {
340 return this.stripOutlineStroke;
341 }
342
343 /**
344 * Sets the stroke used to draw the outline around the paint strip and
345 * sends a {@link TitleChangeEvent} to all registered listeners.
346 *
347 * @param stroke the stroke (<code>null</code> not permitted).
348 *
349 * @see #getStripOutlineStroke()
350 */
351 public void setStripOutlineStroke(Stroke stroke) {
352 if (stroke == null) {
353 throw new IllegalArgumentException("Null 'stroke' argument.");
354 }
355 this.stripOutlineStroke = stroke;
356 notifyListeners(new TitleChangeEvent(this));
357 }
358
359 /**
360 * Returns the background paint.
361 *
362 * @return The background paint.
363 */
364 public Paint getBackgroundPaint() {
365 return this.backgroundPaint;
366 }
367
368 /**
369 * Sets the background paint and sends a {@link TitleChangeEvent} to all
370 * registered listeners.
371 *
372 * @param paint the paint (<code>null</code> permitted).
373 */
374 public void setBackgroundPaint(Paint paint) {
375 this.backgroundPaint = paint;
376 notifyListeners(new TitleChangeEvent(this));
377 }
378
379 /**
380 * Returns the number of subdivisions used to draw the scale.
381 *
382 * @return The subdivision count.
383 *
384 * @since 1.0.11
385 */
386 public int getSubdivisionCount() {
387 return this.subdivisions;
388 }
389
390 /**
391 * Sets the subdivision count and sends a {@link TitleChangeEvent} to
392 * all registered listeners.
393 *
394 * @param count the count.
395 *
396 * @since 1.0.11
397 */
398 public void setSubdivisionCount(int count) {
399 if (count <= 0) {
400 throw new IllegalArgumentException("Requires 'count' > 0.");
401 }
402 this.subdivisions = count;
403 notifyListeners(new TitleChangeEvent(this));
404 }
405
406 /**
407 * Receives notification of an axis change event and responds by firing
408 * a title change event.
409 *
410 * @param event the event.
411 *
412 * @since 1.0.13
413 */
414 public void axisChanged(AxisChangeEvent event) {
415 if (this.axis == event.getAxis()) {
416 notifyListeners(new TitleChangeEvent(this));
417 }
418 }
419
420 /**
421 * Arranges the contents of the block, within the given constraints, and
422 * returns the block size.
423 *
424 * @param g2 the graphics device.
425 * @param constraint the constraint (<code>null</code> not permitted).
426 *
427 * @return The block size (in Java2D units, never <code>null</code>).
428 */
429 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
430 RectangleConstraint cc = toContentConstraint(constraint);
431 LengthConstraintType w = cc.getWidthConstraintType();
432 LengthConstraintType h = cc.getHeightConstraintType();
433 Size2D contentSize = null;
434 if (w == LengthConstraintType.NONE) {
435 if (h == LengthConstraintType.NONE) {
436 contentSize = new Size2D(getWidth(), getHeight());
437 }
438 else if (h == LengthConstraintType.RANGE) {
439 throw new RuntimeException("Not yet implemented.");
440 }
441 else if (h == LengthConstraintType.FIXED) {
442 throw new RuntimeException("Not yet implemented.");
443 }
444 }
445 else if (w == LengthConstraintType.RANGE) {
446 if (h == LengthConstraintType.NONE) {
447 throw new RuntimeException("Not yet implemented.");
448 }
449 else if (h == LengthConstraintType.RANGE) {
450 contentSize = arrangeRR(g2, cc.getWidthRange(),
451 cc.getHeightRange());
452 }
453 else if (h == LengthConstraintType.FIXED) {
454 throw new RuntimeException("Not yet implemented.");
455 }
456 }
457 else if (w == LengthConstraintType.FIXED) {
458 if (h == LengthConstraintType.NONE) {
459 throw new RuntimeException("Not yet implemented.");
460 }
461 else if (h == LengthConstraintType.RANGE) {
462 throw new RuntimeException("Not yet implemented.");
463 }
464 else if (h == LengthConstraintType.FIXED) {
465 throw new RuntimeException("Not yet implemented.");
466 }
467 }
468 return new Size2D(calculateTotalWidth(contentSize.getWidth()),
469 calculateTotalHeight(contentSize.getHeight()));
470 }
471
472 /**
473 * Returns the content size for the title. This will reflect the fact that
474 * a text title positioned on the left or right of a chart will be rotated
475 * 90 degrees.
476 *
477 * @param g2 the graphics device.
478 * @param widthRange the width range.
479 * @param heightRange the height range.
480 *
481 * @return The content size.
482 */
483 protected Size2D arrangeRR(Graphics2D g2, Range widthRange,
484 Range heightRange) {
485
486 RectangleEdge position = getPosition();
487 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) {
488
489
490 float maxWidth = (float) widthRange.getUpperBound();
491
492 // determine the space required for the axis
493 AxisSpace space = this.axis.reserveSpace(g2, null,
494 new Rectangle2D.Double(0, 0, maxWidth, 100),
495 RectangleEdge.BOTTOM, null);
496
497 return new Size2D(maxWidth, this.stripWidth + this.axisOffset
498 + space.getTop() + space.getBottom());
499 }
500 else if (position == RectangleEdge.LEFT || position
501 == RectangleEdge.RIGHT) {
502 float maxHeight = (float) heightRange.getUpperBound();
503 AxisSpace space = this.axis.reserveSpace(g2, null,
504 new Rectangle2D.Double(0, 0, 100, maxHeight),
505 RectangleEdge.RIGHT, null);
506 return new Size2D(this.stripWidth + this.axisOffset
507 + space.getLeft() + space.getRight(), maxHeight);
508 }
509 else {
510 throw new RuntimeException("Unrecognised position.");
511 }
512 }
513
514 /**
515 * Draws the legend within the specified area.
516 *
517 * @param g2 the graphics target (<code>null</code> not permitted).
518 * @param area the drawing area (<code>null</code> not permitted).
519 */
520 public void draw(Graphics2D g2, Rectangle2D area) {
521 draw(g2, area, null);
522 }
523
524 /**
525 * Draws the legend within the specified area.
526 *
527 * @param g2 the graphics target (<code>null</code> not permitted).
528 * @param area the drawing area (<code>null</code> not permitted).
529 * @param params drawing parameters (ignored here).
530 *
531 * @return <code>null</code>.
532 */
533 public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
534
535 Rectangle2D target = (Rectangle2D) area.clone();
536 target = trimMargin(target);
537 if (this.backgroundPaint != null) {
538 g2.setPaint(this.backgroundPaint);
539 g2.fill(target);
540 }
541 getFrame().draw(g2, target);
542 getFrame().getInsets().trim(target);
543 target = trimPadding(target);
544 double base = this.axis.getLowerBound();
545 double increment = this.axis.getRange().getLength() / this.subdivisions;
546 Rectangle2D r = new Rectangle2D.Double();
547
548 if (RectangleEdge.isTopOrBottom(getPosition())) {
549 RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
550 this.axisLocation, PlotOrientation.HORIZONTAL);
551 if (axisEdge == RectangleEdge.TOP) {
552 for (int i = 0; i < this.subdivisions; i++) {
553 double v = base + (i * increment);
554 Paint p = this.scale.getPaint(v);
555 double vv0 = this.axis.valueToJava2D(v, target,
556 RectangleEdge.TOP);
557 double vv1 = this.axis.valueToJava2D(v + increment, target,
558 RectangleEdge.TOP);
559 double ww = Math.abs(vv1 - vv0) + 1.0;
560 r.setRect(Math.min(vv0, vv1), target.getMaxY()
561 - this.stripWidth, ww, this.stripWidth);
562 g2.setPaint(p);
563 g2.fill(r);
564 }
565 if (isStripOutlineVisible()) {
566 g2.setPaint(this.stripOutlinePaint);
567 g2.setStroke(this.stripOutlineStroke);
568 g2.draw(new Rectangle2D.Double(target.getMinX(),
569 target.getMaxY() - this.stripWidth,
570 target.getWidth(), this.stripWidth));
571 }
572 this.axis.draw(g2, target.getMaxY() - this.stripWidth
573 - this.axisOffset, target, target, RectangleEdge.TOP,
574 null);
575 }
576 else if (axisEdge == RectangleEdge.BOTTOM) {
577 for (int i = 0; i < this.subdivisions; i++) {
578 double v = base + (i * increment);
579 Paint p = this.scale.getPaint(v);
580 double vv0 = this.axis.valueToJava2D(v, target,
581 RectangleEdge.BOTTOM);
582 double vv1 = this.axis.valueToJava2D(v + increment, target,
583 RectangleEdge.BOTTOM);
584 double ww = Math.abs(vv1 - vv0) + 1.0;
585 r.setRect(Math.min(vv0, vv1), target.getMinY(), ww,
586 this.stripWidth);
587 g2.setPaint(p);
588 g2.fill(r);
589 }
590 if (isStripOutlineVisible()) {
591 g2.setPaint(this.stripOutlinePaint);
592 g2.setStroke(this.stripOutlineStroke);
593 g2.draw(new Rectangle2D.Double(target.getMinX(),
594 target.getMinY(), target.getWidth(),
595 this.stripWidth));
596 }
597 this.axis.draw(g2, target.getMinY() + this.stripWidth
598 + this.axisOffset, target, target,
599 RectangleEdge.BOTTOM, null);
600 }
601 }
602 else {
603 RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
604 this.axisLocation, PlotOrientation.VERTICAL);
605 if (axisEdge == RectangleEdge.LEFT) {
606 for (int i = 0; i < this.subdivisions; i++) {
607 double v = base + (i * increment);
608 Paint p = this.scale.getPaint(v);
609 double vv0 = this.axis.valueToJava2D(v, target,
610 RectangleEdge.LEFT);
611 double vv1 = this.axis.valueToJava2D(v + increment, target,
612 RectangleEdge.LEFT);
613 double hh = Math.abs(vv1 - vv0) + 1.0;
614 r.setRect(target.getMaxX() - this.stripWidth,
615 Math.min(vv0, vv1), this.stripWidth, hh);
616 g2.setPaint(p);
617 g2.fill(r);
618 }
619 if (isStripOutlineVisible()) {
620 g2.setPaint(this.stripOutlinePaint);
621 g2.setStroke(this.stripOutlineStroke);
622 g2.draw(new Rectangle2D.Double(target.getMaxX()
623 - this.stripWidth, target.getMinY(), this.stripWidth,
624 target.getHeight()));
625 }
626 this.axis.draw(g2, target.getMaxX() - this.stripWidth
627 - this.axisOffset, target, target, RectangleEdge.LEFT,
628 null);
629 }
630 else if (axisEdge == RectangleEdge.RIGHT) {
631 for (int i = 0; i < this.subdivisions; i++) {
632 double v = base + (i * increment);
633 Paint p = this.scale.getPaint(v);
634 double vv0 = this.axis.valueToJava2D(v, target,
635 RectangleEdge.LEFT);
636 double vv1 = this.axis.valueToJava2D(v + increment, target,
637 RectangleEdge.LEFT);
638 double hh = Math.abs(vv1 - vv0) + 1.0;
639 r.setRect(target.getMinX(), Math.min(vv0, vv1),
640 this.stripWidth, hh);
641 g2.setPaint(p);
642 g2.fill(r);
643 }
644 if (isStripOutlineVisible()) {
645 g2.setPaint(this.stripOutlinePaint);
646 g2.setStroke(this.stripOutlineStroke);
647 g2.draw(new Rectangle2D.Double(target.getMinX(),
648 target.getMinY(), this.stripWidth,
649 target.getHeight()));
650 }
651 this.axis.draw(g2, target.getMinX() + this.stripWidth
652 + this.axisOffset, target, target, RectangleEdge.RIGHT,
653 null);
654 }
655 }
656 return null;
657 }
658
659 /**
660 * Tests this legend for equality with an arbitrary object.
661 *
662 * @param obj the object (<code>null</code> permitted).
663 *
664 * @return A boolean.
665 */
666 public boolean equals(Object obj) {
667 if (!(obj instanceof PaintScaleLegend)) {
668 return false;
669 }
670 PaintScaleLegend that = (PaintScaleLegend) obj;
671 if (!this.scale.equals(that.scale)) {
672 return false;
673 }
674 if (!this.axis.equals(that.axis)) {
675 return false;
676 }
677 if (!this.axisLocation.equals(that.axisLocation)) {
678 return false;
679 }
680 if (this.axisOffset != that.axisOffset) {
681 return false;
682 }
683 if (this.stripWidth != that.stripWidth) {
684 return false;
685 }
686 if (this.stripOutlineVisible != that.stripOutlineVisible) {
687 return false;
688 }
689 if (!PaintUtilities.equal(this.stripOutlinePaint,
690 that.stripOutlinePaint)) {
691 return false;
692 }
693 if (!this.stripOutlineStroke.equals(that.stripOutlineStroke)) {
694 return false;
695 }
696 if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) {
697 return false;
698 }
699 if (this.subdivisions != that.subdivisions) {
700 return false;
701 }
702 return super.equals(obj);
703 }
704
705 /**
706 * Provides serialization support.
707 *
708 * @param stream the output stream.
709 *
710 * @throws IOException if there is an I/O error.
711 */
712 private void writeObject(ObjectOutputStream stream) throws IOException {
713 stream.defaultWriteObject();
714 SerialUtilities.writePaint(this.backgroundPaint, stream);
715 SerialUtilities.writePaint(this.stripOutlinePaint, stream);
716 SerialUtilities.writeStroke(this.stripOutlineStroke, stream);
717 }
718
719 /**
720 * Provides serialization support.
721 *
722 * @param stream the input stream.
723 *
724 * @throws IOException if there is an I/O error.
725 * @throws ClassNotFoundException if there is a classpath problem.
726 */
727 private void readObject(ObjectInputStream stream)
728 throws IOException, ClassNotFoundException {
729 stream.defaultReadObject();
730 this.backgroundPaint = SerialUtilities.readPaint(stream);
731 this.stripOutlinePaint = SerialUtilities.readPaint(stream);
732 this.stripOutlineStroke = SerialUtilities.readStroke(stream);
733 }
734
735 }