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