KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > DefaultOldLegend


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * ---------------------
27  * DefaultOldLegend.java
28  * ---------------------
29  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
30  *
31  * Original Author: David Gilbert (for Object Refinery Limited);
32  * Contributor(s): Andrzej Porebski;
33  * Luke Quinane;
34  * Barak Naveh;
35  *
36  * $Id: DefaultOldLegend.java,v 1.5 2005/05/19 15:40:56 mungady Exp $
37  *
38  * Changes (from 20-Jun-2001)
39  * --------------------------
40  * 20-Jun-2001 : Modifications submitted by Andrzej Porebski for legend
41  * placement;
42  * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
43  * 16-Oct-2001 : Moved data source classes to com.jrefinery.data.* (DG);
44  * 19-Oct-2001 : Moved some methods [getSeriesPaint() etc.] from JFreeChart to
45  * Plot (DG);
46  * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
47  * 06-Feb-2002 : Bug fix for legends in small areas (DG);
48  * 23-Apr-2002 : Legend item labels are now obtained from the plot, not the
49  * chart (DG);
50  * 20-Jun-2002 : Added outline paint and stroke attributes for the key
51  * boxes (DG);
52  * 18-Sep-2002 : Fixed errors reported by Checkstyle (DG);
53  * 23-Sep-2002 : Changed the name of LegendItem --> DrawableLegendItem (DG);
54  * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
55  * 16-Oct-2002 : Adjusted vertical text position in legend item (DG);
56  * 17-Oct-2002 : Fixed bug where legend items are not using the font that has
57  * been set (DG);
58  * 11-Feb-2003 : Added title code by Donald Mitchell, removed unnecessary
59  * constructor (DG);
60  * 26-Mar-2003 : Implemented Serializable (DG);
61  * 22-Sep-2003 : Added nullpointer checks (TM);
62  * 23-Sep-2003 : Fixed bug in equals() method (DG);
63  * 08-Oct-2003 : Applied patch for displaying series line style, contributed
64  * by Luke Quinane (DG);
65  * 23-Dec-2003 : Added scale factors (x and y) for shapes displayed in
66  * legend (DG);
67  * 26-Mar-2004 : Added option to control item order, contributed by Angel (DG);
68  * 26-Mar-2004 : Added support for 8 more anchor points (BN);
69  * 27-Mar-2004 : Added support for round corners of bounding box (BN);
70  * 07-Apr-2004 : Changed text bounds calculation (DG);
71  * 21-Apr-2004 : Barak Naveh has contributed word-wrapping for legend
72  * items (BN);
73  * 25-Nov-2004 : Small update due to changes in LegendItem class (DG);
74  * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
75  * 27-Jan-2005 : Legend now picks up more info from the renderer of each
76  * series (DG);
77  * 22-Feb-2005 : Renamed outerGap --> margin (DG);
78  * 28-Mar-2005 : Renamed StandardLegend --> DefaultOldLegend (DG);
79  * 20-Apr-2005 : Updated for changes in LegendItem (DG);
80  * 26-Apr-2005 : Removed LOGGER (DG);
81  *
82  */

83
84 package org.jfree.chart;
85
86 import java.awt.BasicStroke JavaDoc;
87 import java.awt.Color JavaDoc;
88 import java.awt.Font JavaDoc;
89 import java.awt.FontMetrics JavaDoc;
90 import java.awt.Graphics2D JavaDoc;
91 import java.awt.Paint JavaDoc;
92 import java.awt.Shape JavaDoc;
93 import java.awt.Stroke JavaDoc;
94 import java.awt.font.LineMetrics JavaDoc;
95 import java.awt.geom.AffineTransform JavaDoc;
96 import java.awt.geom.Line2D JavaDoc;
97 import java.awt.geom.Point2D JavaDoc;
98 import java.awt.geom.Rectangle2D JavaDoc;
99 import java.awt.geom.RectangularShape JavaDoc;
100 import java.awt.geom.RoundRectangle2D JavaDoc;
101 import java.io.IOException JavaDoc;
102 import java.io.ObjectInputStream JavaDoc;
103 import java.io.ObjectOutputStream JavaDoc;
104 import java.io.Serializable JavaDoc;
105 import java.util.ArrayList JavaDoc;
106 import java.util.Iterator JavaDoc;
107 import java.util.List JavaDoc;
108
109 import org.jfree.chart.entity.EntityCollection;
110 import org.jfree.chart.entity.LegendItemEntity;
111 import org.jfree.chart.event.LegendChangeEvent;
112 import org.jfree.io.SerialUtilities;
113 import org.jfree.text.TextUtilities;
114 import org.jfree.ui.RectangleInsets;
115 import org.jfree.ui.TextAnchor;
116 import org.jfree.util.ObjectUtilities;
117
118 /**
119  * A chart legend shows the names and visual representations of the series
120  * that are plotted in a chart. If possible, use
121  * {@link org.jfree.chart.title.LegendTitle} rather than this class.
122  */

123 public class DefaultOldLegend extends OldLegend implements Serializable JavaDoc {
124
125     /** For serialization. */
126     private static final long serialVersionUID = -5466149184220837922L;
127     
128     /** The default margin (space around the outside of the legend). */
129     public static final RectangleInsets DEFAULT_MARGIN
130         = new RectangleInsets(3, 3, 3, 3);
131
132     /** The default inner gap. */
133     public static final RectangleInsets DEFAULT_PADDING
134         = new RectangleInsets(2, 2, 2, 2);
135
136     /** The default outline stroke. */
137     public static final Stroke JavaDoc DEFAULT_OUTLINE_STROKE = new BasicStroke JavaDoc();
138
139     /** The default outline paint. */
140     public static final Paint JavaDoc DEFAULT_OUTLINE_PAINT = Color.gray;
141
142     /** The default background paint. */
143     public static final Paint JavaDoc DEFAULT_BACKGROUND_PAINT = Color.white;
144
145     /** The default title font. */
146     public static final Font JavaDoc DEFAULT_TITLE_FONT
147         = new Font JavaDoc("SansSerif", Font.BOLD, 11);
148
149     /** The default item font. */
150     public static final Font JavaDoc DEFAULT_ITEM_FONT
151         = new Font JavaDoc("SansSerif", Font.PLAIN, 10);
152
153     /**
154      * Used with {@link #setPreferredWidth(double)} to indicate that no
155      * preferred width is desired and defaults are to be used.
156      */

157     public static final double NO_PREFERRED_WIDTH = Double.MAX_VALUE;
158     
159     /** Reported when illegal legend is unexpectedly found. */
160     private static final String JavaDoc UNEXPECTED_LEGEND_ANCHOR
161         = "Unexpected legend anchor";
162     
163     /** The amount of blank space around the legend. */
164     private RectangleInsets margin;
165
166     /** The stroke used to draw the outline of the legend. */
167     private transient Stroke JavaDoc outlineStroke;
168
169     /** The paint used to draw the outline of the legend. */
170     private transient Paint JavaDoc outlinePaint;
171
172     /** The paint used to draw the background of the legend. */
173     private transient Paint JavaDoc backgroundPaint;
174
175     /** The blank space inside the legend box. */
176     private RectangleInsets padding;
177
178     /** An optional title for the legend. */
179     private String JavaDoc title;
180
181     /** The font used to display the legend title. */
182     private Font JavaDoc titleFont;
183
184     /** The font used to display the legend item names. */
185     private Font JavaDoc itemFont;
186
187     /** The paint used to display the legend item names. */
188     private transient Paint JavaDoc itemPaint;
189   
190     /** The x scale factor for shapes displayed in the legend. */
191     private double shapeScaleX = 1.0;
192     
193     /** The y scale factor for shapes displayed in the legend. */
194     private double shapeScaleY = 1.0;
195
196     /** The order of the legend items. */
197     private LegendRenderingOrder renderingOrder = LegendRenderingOrder.STANDARD;
198     
199     /**
200      * The width of the arc used to round off the corners of the bounding box.
201      */

202     private int boundingBoxArcWidth = 0;
203
204     /**
205      * The height of the arc used to round off the corners of the bounding box.
206      */

207     private int boundingBoxArcHeight = 0;
208
209     /** The preferred width of the legend bounding box. */
210     private double preferredWidth = NO_PREFERRED_WIDTH;
211     
212     /**
213      * Constructs a new legend with default settings.
214      */

215     public DefaultOldLegend() {
216         this.margin = DEFAULT_MARGIN;
217         this.padding = DEFAULT_PADDING;
218         this.backgroundPaint = DEFAULT_BACKGROUND_PAINT;
219         this.outlineStroke = DEFAULT_OUTLINE_STROKE;
220         this.outlinePaint = DEFAULT_OUTLINE_PAINT;
221         this.title = null;
222         this.titleFont = DEFAULT_TITLE_FONT;
223         this.itemFont = DEFAULT_ITEM_FONT;
224         this.itemPaint = Color.black;
225     }
226
227     /**
228      * Returns the margin for the legend. This is the amount of blank space
229      * around the outside of the legend.
230      *
231      * @return The gap (never <code>null</code>).
232      */

233     public RectangleInsets getMargin() {
234         return this.margin;
235     }
236
237     /**
238      * Sets the margin for the legend and sends a {@link LegendChangeEvent} to
239      * all registered listeners.
240      *
241      * @param margin the margin (<code>null</code> not permitted).
242      */

243     public void setMargin(RectangleInsets margin) {
244         if (margin == null) {
245             throw new NullPointerException JavaDoc("Null 'margin' argument.");
246         }
247         this.margin = margin;
248         notifyListeners(new LegendChangeEvent(this));
249     }
250
251     /**
252      * Returns the padding for the legend. This is the amount of blank space
253      * around the inside of the legend.
254      *
255      * @return The gap (never <code>null</code>).
256      */

257     public RectangleInsets getPadding() {
258         return this.padding;
259     }
260
261     /**
262      * Sets the padding for the legend and sends a {@link LegendChangeEvent}
263      * to all registered listeners.
264      *
265      * @param padding the padding (<code>null</code> not permitted).
266      */

267     public void setPadding(RectangleInsets padding) {
268         if (padding == null) {
269             throw new NullPointerException JavaDoc("Null 'padding' argument.");
270         }
271         this.padding = padding;
272         notifyListeners(new LegendChangeEvent(this));
273     }
274
275     /**
276      * Returns the background paint for the legend.
277      *
278      * @return The background paint (never <code>null</code>).
279      */

280     public Paint JavaDoc getBackgroundPaint() {
281         return this.backgroundPaint;
282     }
283
284     /**
285      * Sets the background paint for the legend and sends a
286      * {@link LegendChangeEvent} to all registered listeners.
287      *
288      * @param paint the paint (<code>null</code> not permitted).
289      */

290     public void setBackgroundPaint(Paint JavaDoc paint) {
291         if (paint == null) {
292             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
293         }
294         this.backgroundPaint = paint;
295         notifyListeners(new LegendChangeEvent(this));
296     }
297
298     /**
299      * Returns the outline stroke.
300      *
301      * @return The outline stroke (never <code>null</code>).
302      */

303     public Stroke JavaDoc getOutlineStroke() {
304         return this.outlineStroke;
305     }
306
307     /**
308      * Sets the outline stroke and sends a {@link LegendChangeEvent} to all
309      * registered listeners.
310      *
311      * @param stroke the stroke (<code>null</code> not permitted).
312      */

313     public void setOutlineStroke(Stroke JavaDoc stroke) {
314         if (stroke == null) {
315             throw new NullPointerException JavaDoc("Null 'stroke' argument.");
316         }
317         this.outlineStroke = stroke;
318         notifyListeners(new LegendChangeEvent(this));
319     }
320
321     /**
322      * Returns the outline paint.
323      *
324      * @return The outline paint (never <code>null</code>).
325      */

326     public Paint JavaDoc getOutlinePaint() {
327         return this.outlinePaint;
328     }
329
330     /**
331      * Sets the outline paint and sends a {@link LegendChangeEvent} to all
332      * registered listeners.
333      *
334      * @param paint the paint (<code>null</code> not permitted).
335      */

336     public void setOutlinePaint(Paint JavaDoc paint) {
337         if (paint == null) {
338             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
339         }
340         this.outlinePaint = paint;
341         notifyListeners(new LegendChangeEvent(this));
342     }
343
344     /**
345      * Gets the title for the legend.
346      *
347      * @return The title (possibly <code>null</code>).
348      */

349     public String JavaDoc getTitle() {
350         return this.title;
351     }
352
353     /**
354      * Sets the title of the legend and sends a {@link LegendChangeEvent} to
355      * all registered listeners.
356      *
357      * @param title the title (<code>null</code> permitted).
358      */

359     public void setTitle(String JavaDoc title) {
360         this.title = title;
361         notifyListeners(new LegendChangeEvent(this));
362     }
363
364     /**
365      * Returns the title font.
366      *
367      * @return The font (never <code>null</code>).
368      */

369     public Font JavaDoc getTitleFont() {
370         return this.titleFont;
371     }
372
373     /**
374      * Sets the title font and sends a {@link LegendChangeEvent} to all
375      * registered listeners.
376      *
377      * @param font the font (<code>null</code> not permitted).
378      */

379     public void setTitleFont(Font JavaDoc font) {
380         if (font == null) {
381             throw new IllegalArgumentException JavaDoc("Null 'font' argument.");
382         }
383         this.titleFont = font;
384         notifyListeners(new LegendChangeEvent(this));
385     }
386
387     /**
388      * Returns the series label font.
389      *
390      * @return The font (never <code>null</code>).
391      */

392     public Font JavaDoc getItemFont() {
393         return this.itemFont;
394     }
395
396     /**
397      * Sets the series label font and sends a {@link LegendChangeEvent} to all
398      * registered listeners.
399      *
400      * @param font the font (<code>null</code> not permitted).
401      */

402     public void setItemFont(Font JavaDoc font) {
403         if (font == null) {
404             throw new IllegalArgumentException JavaDoc("Null 'font' argument.");
405         }
406         this.itemFont = font;
407         notifyListeners(new LegendChangeEvent(this));
408     }
409
410     /**
411      * Returns the series label paint.
412      *
413      * @return The paint (never <code>null</code>).
414      */

415     public Paint JavaDoc getItemPaint() {
416         return this.itemPaint;
417     }
418
419     /**
420      * Sets the series label paint and sends a {@link LegendChangeEvent} to all
421      * registered listeners.
422      *
423      * @param paint the paint (<code>null</code> not permitted).
424      */

425     public void setItemPaint(Paint JavaDoc paint) {
426         if (paint == null) {
427             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
428         }
429         this.itemPaint = paint;
430         notifyListeners(new LegendChangeEvent(this));
431     }
432
433     /**
434      * Returns the x scale factor for shapes displayed in the legend.
435      *
436      * @return The x scale factor.
437      */

438     public double getShapeScaleX() {
439         return this.shapeScaleX;
440     }
441     
442     /**
443      * Sets the x scale factor for shapes displayed in the legend and sends a
444      * {@link LegendChangeEvent} to all registered listeners.
445      *
446      * @param factor the factor.
447      */

448     public void setShapeScaleX(double factor) {
449         this.shapeScaleX = factor;
450         notifyListeners(new LegendChangeEvent(this));
451     }
452
453     /**
454      * Returns the y scale factor for shapes displayed in the legend.
455      *
456      * @return The y scale factor.
457      */

458     public double getShapeScaleY() {
459         return this.shapeScaleY;
460     }
461     
462     /**
463      * Sets the y scale factor for shapes displayed in the legend and sends a
464      * {@link LegendChangeEvent} to all registered listeners.
465      *
466      * @param factor the factor.
467      */

468     public void setShapeScaleY(double factor) {
469         this.shapeScaleY = factor;
470         notifyListeners(new LegendChangeEvent(this));
471     }
472
473     /**
474      * Returns the legend rendering order.
475      *
476      * @return The order (never <code>null</code>).
477      */

478     public LegendRenderingOrder getRenderingOrder() {
479         return this.renderingOrder;
480     }
481
482     /**
483      * Sets the legend rendering order and sends a {@link LegendChangeEvent} to
484      * all registered listeners.
485      *
486      * @param order the order (<code>null</code> not permitted).
487      */

488     public void setRenderingOrder(LegendRenderingOrder order) {
489         if (order == null) {
490             throw new IllegalArgumentException JavaDoc("Null 'order' argument.");
491         }
492         this.renderingOrder = order;
493         notifyListeners(new LegendChangeEvent(this));
494     }
495
496     
497     /**
498      * Returns the width of the arc used to round off the corners of the
499      * bounding box.
500      *
501      * @return The width of the arc used to round off the corners of the
502      * bounding box.
503      */

504     public int getBoundingBoxArcWidth() {
505         return this.boundingBoxArcWidth;
506     }
507     
508     /**
509      * Sets the width of the arc used to round off the corners of the
510      * bounding box.
511      * A {@link LegendChangeEvent} is sent to all registered listeners.
512      *
513      * @param arcWidth the new arc width.
514      */

515     public void setBoundingBoxArcWidth(int arcWidth) {
516         this.boundingBoxArcWidth = arcWidth;
517         notifyListeners(new LegendChangeEvent(this));
518     }
519     
520     /**
521      * Returns the height of the arc used to round off the corners of the
522      * bounding box.
523      *
524      * @return The height of the arc used to round off the corners of the
525      * bounding box.
526      */

527     public int getBoundingBoxArcHeight() {
528         return this.boundingBoxArcHeight;
529     }
530     
531     /**
532      * Sets the height of the arc used to round off the corners of the
533      * bounding box.
534      * A {@link LegendChangeEvent} is sent to all registered listeners.
535      *
536      * @param arcHeight the new arc height.
537      */

538     public void setBoundingBoxArcHeight(int arcHeight) {
539         this.boundingBoxArcHeight = arcHeight;
540         notifyListeners(new LegendChangeEvent(this));
541     }
542
543     /**
544      * Returns the preferred width of the legend bounding box if such width
545      * has been defined; otherwise returns <code>NO_PREFERRED_WIDTH</code>.
546      *
547      * @return The preferred width of the legend bounding box if such width
548      * has been defined; otherwise returns
549      * <code>NO_PREFERRED_WIDTH</code>.
550      */

551     public double getPreferredWidth() {
552         return this.preferredWidth;
553     }
554
555     /**
556      * Sets the preferred width of the legend bounding box. If a preferred
557      * width is set, the legend text is word-wrapped in an attempt to fulfill
558      * the preferred width. If the preferred width cannot be fulfilled, the
559      * legend would be wider to the extent necessary.
560      *
561      * <p>
562      * The preferred width takes effect only when the legend's anchor is set to
563      * one of the three EAST anchors or to one of the three WEST anchors.
564      *
565      * <p>
566      * A {@link LegendChangeEvent} is sent to all registered listeners.
567      *
568      * @param width the new width.
569      */

570     public void setPreferredWidth(double width) {
571         this.preferredWidth = width;
572         notifyListeners(new LegendChangeEvent(this));
573     }
574     
575     /**
576      * Draws the legend on a Java 2D graphics device (such as the screen or a
577      * printer).
578      *
579      * @param g2 the graphics device.
580      * @param available the area within which the legend, and afterwards the
581      * plot, should be drawn.
582      * @param info collects rendering info (optional).
583      *
584      * @return The area NOT used by the legend.
585      */

586     public Rectangle2D JavaDoc draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc available,
587                             ChartRenderingInfo info) {
588
589         return draw(
590             g2, available, (getAnchor() & HORIZONTAL) != 0,
591             (getAnchor() & INVERTED) != 0, info
592         );
593
594     }
595
596     /**
597      * Draws the legend.
598      *
599      * @param g2 the graphics device.
600      * @param available the area available for drawing the chart.
601      * @param horizontal a flag indicating whether the legend items are laid
602      * out horizontally.
603      * @param inverted ???
604      * @param info collects rendering info (optional).
605      *
606      * @return The remaining available drawing area.
607      */

608     protected Rectangle2D JavaDoc draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc available,
609                                boolean horizontal, boolean inverted,
610                                ChartRenderingInfo info) {
611
612         LegendItemCollection legendItems
613             = getChart().getPlot().getLegendItems();
614
615         if (legendItems == null || legendItems.getItemCount() == 0) {
616             return available;
617         }
618         // else...
619

620         DrawableLegendItem legendTitle = null;
621         LegendItem titleItem = null;
622
623         if (this.title != null && !this.title.equals("")) {
624             titleItem = new LegendItem(
625                 this.title, this.title, null, null, (Shape JavaDoc) null, Color.black
626             );
627         }
628
629         RectangularShape JavaDoc legendArea;
630         double availableWidth = available.getWidth();
631         // the translation point for the origin of the drawing system
632
Point2D JavaDoc translation;
633
634         // Create buffer for individual items within the legend
635
List JavaDoc items = new ArrayList JavaDoc();
636
637         // Compute individual rectangles in the legend, translation point as
638
// well as the bounding box for the legend.
639
if (horizontal) {
640             double xstart = available.getX()
641                             + getMargin().calculateLeftOutset(availableWidth);
642             double xlimit = available.getMaxX()
643                             - getMargin().calculateRightOutset(availableWidth);
644             double maxRowWidth = 0;
645             double xoffset = 0;
646             double rowHeight = 0;
647             double totalHeight = 0;
648             boolean wrappingAllowed = true;
649
650             if (titleItem != null) {
651                 g2.setFont(getTitleFont());
652
653                 legendTitle = createDrawableLegendItem(
654                     g2, titleItem, xoffset, totalHeight
655                 );
656
657                 rowHeight = Math.max(0, legendTitle.getHeight());
658                 xoffset += legendTitle.getWidth();
659             }
660
661             g2.setFont(this.itemFont);
662             for (int i = 0; i < legendItems.getItemCount(); i++) {
663                 DrawableLegendItem item;
664                 
665                 if (this.renderingOrder == LegendRenderingOrder.STANDARD) {
666                     item = createDrawableLegendItem(
667                         g2, legendItems.get(i), xoffset, totalHeight
668                     );
669                 }
670                 else if (this.renderingOrder == LegendRenderingOrder.REVERSE) {
671                     item = createDrawableLegendItem(
672                         g2, legendItems.get(legendItems.getItemCount() - i - 1),
673                         xoffset, totalHeight
674                     );
675                 }
676                 else {
677                     // we're not supposed to get here, will cause
678
// NullPointerException
679
item = null;
680                 }
681
682                 if (item.getMaxX() + xstart > xlimit && wrappingAllowed) {
683                     // start a new row
684
maxRowWidth = Math.max(maxRowWidth, xoffset);
685                     xoffset = 0;
686                     totalHeight += rowHeight;
687                     i--; // redo this item in the next row
688
// if item to big to fit, we dont want to attempt wrapping
689
// endlessly.
690
// we therefore disable wrapping for at least one item.
691
wrappingAllowed = false;
692                 }
693                 else {
694                     // continue current row
695
rowHeight = Math.max(rowHeight, item.getHeight());
696                     xoffset += item.getWidth();
697                     // we placed an item in this row, re-allow wrapping for
698
// next item.
699
wrappingAllowed = true;
700                     items.add(item);
701                 }
702             }
703
704             maxRowWidth = Math.max(maxRowWidth, xoffset);
705             totalHeight += rowHeight;
706
707             // Create the bounding box
708
legendArea = new RoundRectangle2D.Double JavaDoc(
709                 0, 0, maxRowWidth, totalHeight, this.boundingBoxArcWidth,
710                 this.boundingBoxArcHeight
711             );
712
713             translation = createTranslationPointForHorizontalDraw(
714                 available, inverted, maxRowWidth, totalHeight
715             );
716         }
717         else { // vertical...
718
double totalHeight = 0;
719             double maxWidth = (this.preferredWidth == NO_PREFERRED_WIDTH)
720                 ? 0 : this.preferredWidth;
721             
722             if (titleItem != null) {
723                 g2.setFont(getTitleFont());
724
725                 legendTitle = createDrawableLegendItem(
726                     g2, titleItem, 0, totalHeight
727                 );
728
729                 totalHeight += legendTitle.getHeight();
730                 maxWidth = Math.max(maxWidth, legendTitle.getWidth());
731             }
732
733             g2.setFont(this.itemFont);
734             
735             int legendItemsLength = legendItems.getItemCount();
736             for (int i = 0; i < legendItemsLength; i++) {
737                 List JavaDoc drawableParts;
738                 
739                 if (this.renderingOrder == LegendRenderingOrder.STANDARD) {
740                     drawableParts = createAllDrawableLinesForItem(g2,
741                             legendItems.get(i), 0, totalHeight, maxWidth);
742                 }
743                 else if (this.renderingOrder == LegendRenderingOrder.REVERSE) {
744                     drawableParts = createAllDrawableLinesForItem(
745                         g2, legendItems.get(legendItemsLength - i - 1), 0,
746                         totalHeight, maxWidth
747                     );
748                 }
749                 else {
750                     // we're not supposed to get here, will cause
751
// NullPointerException
752
drawableParts = null;
753                 }
754                 
755                 for (Iterator JavaDoc j = drawableParts.iterator(); j.hasNext();) {
756                     DrawableLegendItem item = (DrawableLegendItem) j.next();
757                     
758                     totalHeight += item.getHeight();
759                     maxWidth = Math.max(maxWidth, item.getWidth());
760                     
761                     items.add(item);
762                 }
763             }
764
765             // Create the bounding box
766
legendArea = new RoundRectangle2D.Float JavaDoc(
767                 0, 0, (float) maxWidth, (float) totalHeight,
768                 this.boundingBoxArcWidth, this.boundingBoxArcHeight
769             );
770
771             translation = createTranslationPointForVerticalDraw(
772                 available, inverted, totalHeight, maxWidth
773             );
774         }
775
776         // Move the origin of the drawing to the appropriate location
777
g2.translate(translation.getX(), translation.getY());
778         drawLegendBox(g2, legendArea);
779         drawLegendTitle(g2, legendTitle);
780         drawSeriesElements(g2, items, translation, info);
781
782         // translate the origin back to what it was prior to drawing the legend
783
g2.translate(-translation.getX(), -translation.getY());
784
785         return calcRemainingDrawingArea(
786             available, horizontal, inverted, legendArea
787         );
788     }
789
790     /**
791      * ???
792      *
793      * @param available the available area.
794      * @param inverted inverted?
795      * @param maxRowWidth the maximum row width.
796      * @param totalHeight the total height.
797      *
798      * @return The translation point.
799      */

800     private Point2D JavaDoc createTranslationPointForHorizontalDraw(
801             Rectangle2D JavaDoc available, boolean inverted, double maxRowWidth,
802             double totalHeight) {
803         // The yloc point is the variable part of the translation point
804
// for horizontal legends xloc can be: left, center or right.
805
double yloc = (inverted)
806             ? available.getMaxY() - totalHeight
807               - getMargin().calculateBottomOutset(available.getHeight())
808             : available.getY()
809               + getMargin().calculateTopOutset(available.getHeight());
810         double xloc;
811         if (isAnchoredToLeft()) {
812             xloc = available.getX()
813                    + getMargin().calculateLeftOutset(available.getWidth());
814         }
815         else if (isAnchoredToCenter()) {
816             xloc = available.getX() + available.getWidth() / 2
817                    - maxRowWidth / 2;
818         }
819         else if (isAnchoredToRight()) {
820             xloc = available.getX() + available.getWidth()
821                    - maxRowWidth - getChart().getPlot().getInsets().getLeft();
822         }
823         else {
824             throw new IllegalStateException JavaDoc(UNEXPECTED_LEGEND_ANCHOR);
825         }
826         
827         // Create the translation point
828
return new Point2D.Double JavaDoc(xloc, yloc);
829     }
830
831     /**
832      * ???
833      *
834      * @param available the available area.
835      * @param inverted inverted?
836      * @param totalHeight the total height.
837      * @param maxWidth the maximum width.
838      *
839      * @return The translation point.
840      */

841     private Point2D JavaDoc createTranslationPointForVerticalDraw(Rectangle2D JavaDoc available,
842             boolean inverted, double totalHeight, double maxWidth) {
843         // The xloc point is the variable part of the translation point
844
// for vertical legends yloc can be: top, middle or bottom.
845
double xloc = (inverted)
846             ? available.getMaxX() - maxWidth
847               - getMargin().calculateRightOutset(available.getWidth())
848             : available.getX()
849               + getMargin().calculateLeftOutset(available.getWidth());
850         double yloc;
851         if (isAnchoredToTop()) {
852             yloc = available.getY() + getChart().getPlot().getInsets().getTop();
853         }
854         else if (isAnchoredToMiddle()) {
855             yloc = available.getY() + (available.getHeight() / 2)
856                    - (totalHeight / 2);
857         }
858         else if (isAnchoredToBottom()) {
859             yloc = available.getY() + available.getHeight()
860                    - getChart().getPlot().getInsets().getBottom() - totalHeight;
861         }
862         else {
863             throw new IllegalStateException JavaDoc(UNEXPECTED_LEGEND_ANCHOR);
864         }
865         // Create the translation point
866
return new Point2D.Double JavaDoc(xloc, yloc);
867     }
868
869     /**
870      * Draws the legend title.
871      *
872      * @param g2 the graphics device (<code>null</code> not permitted).
873      * @param legendTitle the title (<code>null</code> permitted, in which
874      * case the method does nothing).
875      */

876     private void drawLegendTitle(Graphics2D JavaDoc g2,
877