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 * MarkerAxisBand.java
029 * -------------------
030 * (C) Copyright 2000-2007, by Object Refinery Limited.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): -;
034 *
035 * Changes
036 * -------
037 * 03-Sep-2002 : Updated Javadoc comments (DG);
038 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
039 * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
040 * 26-Mar-2003 : Implemented Serializable (DG);
041 * 13-May-2003 : Renamed HorizontalMarkerAxisBand --> MarkerAxisBand (DG);
042 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
043 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
044 * 07-Apr-2004 : Changed text bounds calculation (DG);
045 *
046 */
047
048 package org.jfree.chart.axis;
049
050 import java.awt.AlphaComposite;
051 import java.awt.Color;
052 import java.awt.Composite;
053 import java.awt.Font;
054 import java.awt.FontMetrics;
055 import java.awt.Graphics2D;
056 import java.awt.font.LineMetrics;
057 import java.awt.geom.Rectangle2D;
058 import java.io.Serializable;
059 import java.util.Iterator;
060 import java.util.List;
061
062 import org.jfree.chart.plot.IntervalMarker;
063 import org.jfree.text.TextUtilities;
064 import org.jfree.ui.RectangleEdge;
065 import org.jfree.util.ObjectUtilities;
066
067 /**
068 * A band that can be added to a number axis to display regions.
069 */
070 public class MarkerAxisBand implements Serializable {
071
072 /** For serialization. */
073 private static final long serialVersionUID = -1729482413886398919L;
074
075 /** The axis that the band belongs to. */
076 private NumberAxis axis;
077
078 /** The top outer gap. */
079 private double topOuterGap;
080
081 /** The top inner gap. */
082 private double topInnerGap;
083
084 /** The bottom outer gap. */
085 private double bottomOuterGap;
086
087 /** The bottom inner gap. */
088 private double bottomInnerGap;
089
090 /** The font. */
091 private Font font;
092
093 /** Storage for the markers. */
094 private List markers;
095
096 /**
097 * Constructs a new axis band.
098 *
099 * @param axis the owner.
100 * @param topOuterGap the top outer gap.
101 * @param topInnerGap the top inner gap.
102 * @param bottomOuterGap the bottom outer gap.
103 * @param bottomInnerGap the bottom inner gap.
104 * @param font the font.
105 */
106 public MarkerAxisBand(NumberAxis axis,
107 double topOuterGap, double topInnerGap,
108 double bottomOuterGap, double bottomInnerGap,
109 Font font) {
110 this.axis = axis;
111 this.topOuterGap = topOuterGap;
112 this.topInnerGap = topInnerGap;
113 this.bottomOuterGap = bottomOuterGap;
114 this.bottomInnerGap = bottomInnerGap;
115 this.font = font;
116 this.markers = new java.util.ArrayList();
117 }
118
119 /**
120 * Adds a marker to the band.
121 *
122 * @param marker the marker.
123 */
124 public void addMarker(IntervalMarker marker) {
125 this.markers.add(marker);
126 }
127
128 /**
129 * Returns the height of the band.
130 *
131 * @param g2 the graphics device.
132 *
133 * @return The height of the band.
134 */
135 public double getHeight(Graphics2D g2) {
136
137 double result = 0.0;
138 if (this.markers.size() > 0) {
139 LineMetrics metrics = this.font.getLineMetrics(
140 "123g", g2.getFontRenderContext()
141 );
142 result = this.topOuterGap + this.topInnerGap + metrics.getHeight()
143 + this.bottomInnerGap + this.bottomOuterGap;
144 }
145 return result;
146
147 }
148
149 /**
150 * A utility method that draws a string inside a rectangle.
151 *
152 * @param g2 the graphics device.
153 * @param bounds the rectangle.
154 * @param font the font.
155 * @param text the text.
156 */
157 private void drawStringInRect(Graphics2D g2, Rectangle2D bounds, Font font,
158 String text) {
159
160 g2.setFont(font);
161 FontMetrics fm = g2.getFontMetrics(font);
162 Rectangle2D r = TextUtilities.getTextBounds(text, g2, fm);
163 double x = bounds.getX();
164 if (r.getWidth() < bounds.getWidth()) {
165 x = x + (bounds.getWidth() - r.getWidth()) / 2;
166 }
167 LineMetrics metrics = font.getLineMetrics(
168 text, g2.getFontRenderContext()
169 );
170 g2.drawString(
171 text, (float) x, (float) (bounds.getMaxY()
172 - this.bottomInnerGap - metrics.getDescent())
173 );
174 }
175
176 /**
177 * Draws the band.
178 *
179 * @param g2 the graphics device.
180 * @param plotArea the plot area.
181 * @param dataArea the data area.
182 * @param x the x-coordinate.
183 * @param y the y-coordinate.
184 */
185 public void draw(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea,
186 double x, double y) {
187
188 double h = getHeight(g2);
189 Iterator iterator = this.markers.iterator();
190 while (iterator.hasNext()) {
191 IntervalMarker marker = (IntervalMarker) iterator.next();
192 double start = Math.max(
193 marker.getStartValue(), this.axis.getRange().getLowerBound()
194 );
195 double end = Math.min(
196 marker.getEndValue(), this.axis.getRange().getUpperBound()
197 );
198 double s = this.axis.valueToJava2D(
199 start, dataArea, RectangleEdge.BOTTOM
200 );
201 double e = this.axis.valueToJava2D(
202 end, dataArea, RectangleEdge.BOTTOM
203 );
204 Rectangle2D r = new Rectangle2D.Double(
205 s, y + this.topOuterGap, e - s,
206 h - this.topOuterGap - this.bottomOuterGap
207 );
208
209 Composite originalComposite = g2.getComposite();
210 g2.setComposite(AlphaComposite.getInstance(
211 AlphaComposite.SRC_OVER, marker.getAlpha())
212 );
213 g2.setPaint(marker.getPaint());
214 g2.fill(r);
215 g2.setPaint(marker.getOutlinePaint());
216 g2.draw(r);
217 g2.setComposite(originalComposite);
218
219 g2.setPaint(Color.black);
220 drawStringInRect(g2, r, this.font, marker.getLabel());
221 }
222
223 }
224
225 /**
226 * Tests this axis for equality with another object. Note that the axis
227 * that the band belongs to is ignored in the test.
228 *
229 * @param obj the object (<code>null</code> permitted).
230 *
231 * @return <code>true</code> or <code>false</code>.
232 */
233 public boolean equals(Object obj) {
234 if (obj == this) {
235 return true;
236 }
237 if (!(obj instanceof MarkerAxisBand)) {
238 return false;
239 }
240 MarkerAxisBand that = (MarkerAxisBand) obj;
241 if (this.topOuterGap != that.topOuterGap) {
242 return false;
243 }
244 if (this.topInnerGap != that.topInnerGap) {
245 return false;
246 }
247 if (this.bottomInnerGap != that.bottomInnerGap) {
248 return false;
249 }
250 if (this.bottomOuterGap != that.bottomOuterGap) {
251 return false;
252 }
253 if (!ObjectUtilities.equal(this.font, that.font)) {
254 return false;
255 }
256 if (!ObjectUtilities.equal(this.markers, that.markers)) {
257 return false;
258 }
259 return true;
260 }
261
262 /**
263 * Returns a hash code for the object.
264 *
265 * @return A hash code.
266 */
267 public int hashCode() {
268 int result = 37;
269 result = 19 * result + this.font.hashCode();
270 result = 19 * result + this.markers.hashCode();
271 return result;
272 }
273
274 }