001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, 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 }