001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, 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: * LegendTitle.java
029: * ----------------
030: * (C) Copyright 2002-2007, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): Pierre-Marie Le Biot;
034: *
035: * $Id: LegendTitle.java,v 1.20.2.11 2007/05/18 10:28:33 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 25-Nov-2004 : First working version (DG);
040: * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
041: * 08-Feb-2005 : Updated for changes in RectangleConstraint class (DG);
042: * 11-Feb-2005 : Implemented PublicCloneable (DG);
043: * 23-Feb-2005 : Replaced chart reference with LegendItemSource (DG);
044: * 16-Mar-2005 : Added itemFont attribute (DG);
045: * 17-Mar-2005 : Fixed missing fillShape setting (DG);
046: * 20-Apr-2005 : Added new draw() method (DG);
047: * 03-May-2005 : Modified equals() method to ignore sources (DG);
048: * 13-May-2005 : Added settings for legend item label and graphic padding (DG);
049: * 09-Jun-2005 : Fixed serialization bug (DG);
050: * 01-Sep-2005 : Added itemPaint attribute (PMLB);
051: * ------------- JFREECHART 1.0.x ---------------------------------------------
052: * 20-Jul-2006 : Use new LegendItemBlockContainer to restore support for
053: * LegendItemEntities (DG);
054: * 06-Oct-2006 : Add tooltip and URL text to legend item (DG);
055: * 13-Dec-2006 : Added support for GradientPaint in legend items (DG);
056: * 16-Mar-2007 : Updated border drawing for changes in AbstractBlock (DG);
057: * 18-May-2007 : Pass seriesKey and dataset to legend item block (DG);
058: *
059: */
060:
061: package org.jfree.chart.title;
062:
063: import java.awt.Color;
064: import java.awt.Font;
065: import java.awt.Graphics2D;
066: import java.awt.Paint;
067: import java.awt.geom.Rectangle2D;
068: import java.io.IOException;
069: import java.io.ObjectInputStream;
070: import java.io.ObjectOutputStream;
071: import java.io.Serializable;
072:
073: import org.jfree.chart.LegendItem;
074: import org.jfree.chart.LegendItemCollection;
075: import org.jfree.chart.LegendItemSource;
076: import org.jfree.chart.block.Arrangement;
077: import org.jfree.chart.block.Block;
078: import org.jfree.chart.block.BlockContainer;
079: import org.jfree.chart.block.BlockFrame;
080: import org.jfree.chart.block.BorderArrangement;
081: import org.jfree.chart.block.CenterArrangement;
082: import org.jfree.chart.block.ColumnArrangement;
083: import org.jfree.chart.block.FlowArrangement;
084: import org.jfree.chart.block.LabelBlock;
085: import org.jfree.chart.block.RectangleConstraint;
086: import org.jfree.chart.event.TitleChangeEvent;
087: import org.jfree.io.SerialUtilities;
088: import org.jfree.ui.RectangleAnchor;
089: import org.jfree.ui.RectangleEdge;
090: import org.jfree.ui.RectangleInsets;
091: import org.jfree.ui.Size2D;
092: import org.jfree.util.PaintUtilities;
093: import org.jfree.util.PublicCloneable;
094:
095: /**
096: * A chart title that displays a legend for the data in the chart.
097: * <P>
098: * The title can be populated with legend items manually, or you can assign a
099: * reference to the plot, in which case the legend items will be automatically
100: * created to match the dataset(s).
101: */
102: public class LegendTitle extends Title implements Cloneable,
103: PublicCloneable, Serializable {
104:
105: /** For serialization. */
106: private static final long serialVersionUID = 2644010518533854633L;
107:
108: /** The default item font. */
109: public static final Font DEFAULT_ITEM_FONT = new Font("SansSerif",
110: Font.PLAIN, 12);
111:
112: /** The default item paint. */
113: public static final Paint DEFAULT_ITEM_PAINT = Color.black;
114:
115: /** The sources for legend items. */
116: private LegendItemSource[] sources;
117:
118: /** The background paint (possibly <code>null</code>). */
119: private transient Paint backgroundPaint;
120:
121: /** The edge for the legend item graphic relative to the text. */
122: private RectangleEdge legendItemGraphicEdge;
123:
124: /** The anchor point for the legend item graphic. */
125: private RectangleAnchor legendItemGraphicAnchor;
126:
127: /** The legend item graphic location. */
128: private RectangleAnchor legendItemGraphicLocation;
129:
130: /** The padding for the legend item graphic. */
131: private RectangleInsets legendItemGraphicPadding;
132:
133: /** The item font. */
134: private Font itemFont;
135:
136: /** The item paint. */
137: private transient Paint itemPaint;
138:
139: /** The padding for the item labels. */
140: private RectangleInsets itemLabelPadding;
141:
142: /**
143: * A container that holds and displays the legend items.
144: */
145: private BlockContainer items;
146:
147: private Arrangement hLayout;
148:
149: private Arrangement vLayout;
150:
151: /**
152: * An optional container for wrapping the legend items (allows for adding
153: * a title or other text to the legend).
154: */
155: private BlockContainer wrapper;
156:
157: /**
158: * Constructs a new (empty) legend for the specified source.
159: *
160: * @param source the source.
161: */
162: public LegendTitle(LegendItemSource source) {
163: this (source, new FlowArrangement(), new ColumnArrangement());
164: }
165:
166: /**
167: * Creates a new legend title with the specified arrangement.
168: *
169: * @param source the source.
170: * @param hLayout the horizontal item arrangement (<code>null</code> not
171: * permitted).
172: * @param vLayout the vertical item arrangement (<code>null</code> not
173: * permitted).
174: */
175: public LegendTitle(LegendItemSource source, Arrangement hLayout,
176: Arrangement vLayout) {
177: this .sources = new LegendItemSource[] { source };
178: this .items = new BlockContainer(hLayout);
179: this .hLayout = hLayout;
180: this .vLayout = vLayout;
181: this .backgroundPaint = null;
182: this .legendItemGraphicEdge = RectangleEdge.LEFT;
183: this .legendItemGraphicAnchor = RectangleAnchor.CENTER;
184: this .legendItemGraphicLocation = RectangleAnchor.CENTER;
185: this .legendItemGraphicPadding = new RectangleInsets(2.0, 2.0,
186: 2.0, 2.0);
187: this .itemFont = DEFAULT_ITEM_FONT;
188: this .itemPaint = DEFAULT_ITEM_PAINT;
189: this .itemLabelPadding = new RectangleInsets(2.0, 2.0, 2.0, 2.0);
190: }
191:
192: /**
193: * Returns the legend item sources.
194: *
195: * @return The sources.
196: */
197: public LegendItemSource[] getSources() {
198: return this .sources;
199: }
200:
201: /**
202: * Sets the legend item sources and sends a {@link TitleChangeEvent} to
203: * all registered listeners.
204: *
205: * @param sources the sources (<code>null</code> not permitted).
206: */
207: public void setSources(LegendItemSource[] sources) {
208: if (sources == null) {
209: throw new IllegalArgumentException(
210: "Null 'sources' argument.");
211: }
212: this .sources = sources;
213: notifyListeners(new TitleChangeEvent(this ));
214: }
215:
216: /**
217: * Returns the background paint.
218: *
219: * @return The background paint (possibly <code>null</code>).
220: */
221: public Paint getBackgroundPaint() {
222: return this .backgroundPaint;
223: }
224:
225: /**
226: * Sets the background paint for the legend and sends a
227: * {@link TitleChangeEvent} to all registered listeners.
228: *
229: * @param paint the paint (<code>null</code> permitted).
230: */
231: public void setBackgroundPaint(Paint paint) {
232: this .backgroundPaint = paint;
233: notifyListeners(new TitleChangeEvent(this ));
234: }
235:
236: /**
237: * Returns the location of the shape within each legend item.
238: *
239: * @return The location (never <code>null</code>).
240: */
241: public RectangleEdge getLegendItemGraphicEdge() {
242: return this .legendItemGraphicEdge;
243: }
244:
245: /**
246: * Sets the location of the shape within each legend item.
247: *
248: * @param edge the edge (<code>null</code> not permitted).
249: */
250: public void setLegendItemGraphicEdge(RectangleEdge edge) {
251: if (edge == null) {
252: throw new IllegalArgumentException("Null 'edge' argument.");
253: }
254: this .legendItemGraphicEdge = edge;
255: notifyListeners(new TitleChangeEvent(this ));
256: }
257:
258: /**
259: * Returns the legend item graphic anchor.
260: *
261: * @return The graphic anchor (never <code>null</code>).
262: */
263: public RectangleAnchor getLegendItemGraphicAnchor() {
264: return this .legendItemGraphicAnchor;
265: }
266:
267: /**
268: * Sets the anchor point used for the graphic in each legend item.
269: *
270: * @param anchor the anchor point (<code>null</code> not permitted).
271: */
272: public void setLegendItemGraphicAnchor(RectangleAnchor anchor) {
273: if (anchor == null) {
274: throw new IllegalArgumentException("Null 'anchor' point.");
275: }
276: this .legendItemGraphicAnchor = anchor;
277: }
278:
279: /**
280: * Returns the legend item graphic location.
281: *
282: * @return The location (never <code>null</code>).
283: */
284: public RectangleAnchor getLegendItemGraphicLocation() {
285: return this .legendItemGraphicLocation;
286: }
287:
288: /**
289: * Sets the legend item graphic location.
290: *
291: * @param anchor the anchor (<code>null</code> not permitted).
292: */
293: public void setLegendItemGraphicLocation(RectangleAnchor anchor) {
294: this .legendItemGraphicLocation = anchor;
295: }
296:
297: /**
298: * Returns the padding that will be applied to each item graphic.
299: *
300: * @return The padding (never <code>null</code>).
301: */
302: public RectangleInsets getLegendItemGraphicPadding() {
303: return this .legendItemGraphicPadding;
304: }
305:
306: /**
307: * Sets the padding that will be applied to each item graphic in the
308: * legend and sends a {@link TitleChangeEvent} to all registered listeners.
309: *
310: * @param padding the padding (<code>null</code> not permitted).
311: */
312: public void setLegendItemGraphicPadding(RectangleInsets padding) {
313: if (padding == null) {
314: throw new IllegalArgumentException(
315: "Null 'padding' argument.");
316: }
317: this .legendItemGraphicPadding = padding;
318: notifyListeners(new TitleChangeEvent(this ));
319: }
320:
321: /**
322: * Returns the item font.
323: *
324: * @return The font (never <code>null</code>).
325: */
326: public Font getItemFont() {
327: return this .itemFont;
328: }
329:
330: /**
331: * Sets the item font and sends a {@link TitleChangeEvent} to
332: * all registered listeners.
333: *
334: * @param font the font (<code>null</code> not permitted).
335: */
336: public void setItemFont(Font font) {
337: if (font == null) {
338: throw new IllegalArgumentException("Null 'font' argument.");
339: }
340: this .itemFont = font;
341: notifyListeners(new TitleChangeEvent(this ));
342: }
343:
344: /**
345: * Returns the item paint.
346: *
347: * @return The paint (never <code>null</code>).
348: */
349: public Paint getItemPaint() {
350: return this .itemPaint;
351: }
352:
353: /**
354: * Sets the item paint.
355: *
356: * @param paint the paint (<code>null</code> not permitted).
357: */
358: public void setItemPaint(Paint paint) {
359: if (paint == null) {
360: throw new IllegalArgumentException("Null 'paint' argument.");
361: }
362: this .itemPaint = paint;
363: notifyListeners(new TitleChangeEvent(this ));
364: }
365:
366: /**
367: * Returns the padding used for the items labels.
368: *
369: * @return The padding (never <code>null</code>).
370: */
371: public RectangleInsets getItemLabelPadding() {
372: return this .itemLabelPadding;
373: }
374:
375: /**
376: * Sets the padding used for the item labels in the legend.
377: *
378: * @param padding the padding (<code>null</code> not permitted).
379: */
380: public void setItemLabelPadding(RectangleInsets padding) {
381: if (padding == null) {
382: throw new IllegalArgumentException(
383: "Null 'padding' argument.");
384: }
385: this .itemLabelPadding = padding;
386: notifyListeners(new TitleChangeEvent(this ));
387: }
388:
389: /**
390: * Fetches the latest legend items.
391: */
392: protected void fetchLegendItems() {
393: this .items.clear();
394: RectangleEdge p = getPosition();
395: if (RectangleEdge.isTopOrBottom(p)) {
396: this .items.setArrangement(this .hLayout);
397: } else {
398: this .items.setArrangement(this .vLayout);
399: }
400: for (int s = 0; s < this .sources.length; s++) {
401: LegendItemCollection legendItems = this .sources[s]
402: .getLegendItems();
403: if (legendItems != null) {
404: for (int i = 0; i < legendItems.getItemCount(); i++) {
405: LegendItem item = legendItems.get(i);
406: Block block = createLegendItemBlock(item);
407: this .items.add(block);
408: }
409: }
410: }
411: }
412:
413: /**
414: * Creates a legend item block.
415: *
416: * @param item the legend item.
417: *
418: * @return The block.
419: */
420: protected Block createLegendItemBlock(LegendItem item) {
421: BlockContainer result = null;
422: LegendGraphic lg = new LegendGraphic(item.getShape(), item
423: .getFillPaint());
424: lg.setFillPaintTransformer(item.getFillPaintTransformer());
425: lg.setShapeFilled(item.isShapeFilled());
426: lg.setLine(item.getLine());
427: lg.setLineStroke(item.getLineStroke());
428: lg.setLinePaint(item.getLinePaint());
429: lg.setLineVisible(item.isLineVisible());
430: lg.setShapeVisible(item.isShapeVisible());
431: lg.setShapeOutlineVisible(item.isShapeOutlineVisible());
432: lg.setOutlinePaint(item.getOutlinePaint());
433: lg.setOutlineStroke(item.getOutlineStroke());
434: lg.setPadding(this .legendItemGraphicPadding);
435:
436: LegendItemBlockContainer legendItem = new LegendItemBlockContainer(
437: new BorderArrangement(), item.getDataset(), item
438: .getSeriesKey());
439: lg.setShapeAnchor(getLegendItemGraphicAnchor());
440: lg.setShapeLocation(getLegendItemGraphicLocation());
441: legendItem.add(lg, this .legendItemGraphicEdge);
442: LabelBlock labelBlock = new LabelBlock(item.getLabel(),
443: this .itemFont, this .itemPaint);
444: labelBlock.setPadding(this .itemLabelPadding);
445: legendItem.add(labelBlock);
446: legendItem.setToolTipText(item.getToolTipText());
447: legendItem.setURLText(item.getURLText());
448:
449: result = new BlockContainer(new CenterArrangement());
450: result.add(legendItem);
451:
452: return result;
453: }
454:
455: /**
456: * Returns the container that holds the legend items.
457: *
458: * @return The container for the legend items.
459: */
460: public BlockContainer getItemContainer() {
461: return this .items;
462: }
463:
464: /**
465: * Arranges the contents of the block, within the given constraints, and
466: * returns the block size.
467: *
468: * @param g2 the graphics device.
469: * @param constraint the constraint (<code>null</code> not permitted).
470: *
471: * @return The block size (in Java2D units, never <code>null</code>).
472: */
473: public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
474: Size2D result = new Size2D();
475: fetchLegendItems();
476: if (this .items.isEmpty()) {
477: return result;
478: }
479: BlockContainer container = this .wrapper;
480: if (container == null) {
481: container = this .items;
482: }
483: RectangleConstraint c = toContentConstraint(constraint);
484: Size2D size = container.arrange(g2, c);
485: result.height = calculateTotalHeight(size.height);
486: result.width = calculateTotalWidth(size.width);
487: return result;
488: }
489:
490: /**
491: * Draws the title on a Java 2D graphics device (such as the screen or a
492: * printer).
493: *
494: * @param g2 the graphics device.
495: * @param area the available area for the title.
496: */
497: public void draw(Graphics2D g2, Rectangle2D area) {
498: draw(g2, area, null);
499: }
500:
501: /**
502: * Draws the block within the specified area.
503: *
504: * @param g2 the graphics device.
505: * @param area the area.
506: * @param params ignored (<code>null</code> permitted).
507: *
508: * @return An {@link org.jfree.chart.block.EntityBlockResult} or
509: * <code>null</code>.
510: */
511: public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
512: Rectangle2D target = (Rectangle2D) area.clone();
513: target = trimMargin(target);
514: if (this .backgroundPaint != null) {
515: g2.setPaint(this .backgroundPaint);
516: g2.fill(target);
517: }
518: BlockFrame border = getFrame();
519: border.draw(g2, target);
520: border.getInsets().trim(target);
521: BlockContainer container = this .wrapper;
522: if (container == null) {
523: container = this .items;
524: }
525: target = trimPadding(target);
526: return container.draw(g2, target, params);
527: }
528:
529: /**
530: * Sets the wrapper container for the legend.
531: *
532: * @param wrapper the wrapper container.
533: */
534: public void setWrapper(BlockContainer wrapper) {
535: this .wrapper = wrapper;
536: }
537:
538: /**
539: * Tests this title for equality with an arbitrary object.
540: *
541: * @param obj the object (<code>null</code> permitted).
542: *
543: * @return A boolean.
544: */
545: public boolean equals(Object obj) {
546: if (obj == this ) {
547: return true;
548: }
549: if (!(obj instanceof LegendTitle)) {
550: return false;
551: }
552: if (!super .equals(obj)) {
553: return false;
554: }
555: LegendTitle that = (LegendTitle) obj;
556: if (!PaintUtilities.equal(this .backgroundPaint,
557: that.backgroundPaint)) {
558: return false;
559: }
560: if (this .legendItemGraphicEdge != that.legendItemGraphicEdge) {
561: return false;
562: }
563: if (this .legendItemGraphicAnchor != that.legendItemGraphicAnchor) {
564: return false;
565: }
566: if (this .legendItemGraphicLocation != that.legendItemGraphicLocation) {
567: return false;
568: }
569: if (!this .itemFont.equals(that.itemFont)) {
570: return false;
571: }
572: if (!this .itemPaint.equals(that.itemPaint)) {
573: return false;
574: }
575: if (!this .hLayout.equals(that.hLayout)) {
576: return false;
577: }
578: if (!this .vLayout.equals(that.vLayout)) {
579: return false;
580: }
581: return true;
582: }
583:
584: /**
585: * Provides serialization support.
586: *
587: * @param stream the output stream.
588: *
589: * @throws IOException if there is an I/O error.
590: */
591: private void writeObject(ObjectOutputStream stream)
592: throws IOException {
593: stream.defaultWriteObject();
594: SerialUtilities.writePaint(this .backgroundPaint, stream);
595: SerialUtilities.writePaint(this .itemPaint, stream);
596: }
597:
598: /**
599: * Provides serialization support.
600: *
601: * @param stream the input stream.
602: *
603: * @throws IOException if there is an I/O error.
604: * @throws ClassNotFoundException if there is a classpath problem.
605: */
606: private void readObject(ObjectInputStream stream)
607: throws IOException, ClassNotFoundException {
608: stream.defaultReadObject();
609: this.backgroundPaint = SerialUtilities.readPaint(stream);
610: this.itemPaint = SerialUtilities.readPaint(stream);
611: }
612:
613: }
|