001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 */ 028 029package org.jfree.chart.text; 030 031import java.awt.BasicStroke; 032import java.awt.Color; 033import java.awt.Font; 034import java.awt.Graphics2D; 035import java.awt.Paint; 036import java.awt.Stroke; 037import java.awt.geom.Rectangle2D; 038import java.io.IOException; 039import java.io.ObjectInputStream; 040import java.io.ObjectOutputStream; 041import java.io.Serializable; 042import java.util.Objects; 043import org.jfree.chart.ui.RectangleAnchor; 044import org.jfree.chart.ui.RectangleInsets; 045import org.jfree.chart.ui.Size2D; 046import org.jfree.chart.util.SerialUtils; 047 048/** 049 * A box containing a text block. 050 */ 051public class TextBox implements Serializable { 052 053 /** For serialization. */ 054 private static final long serialVersionUID = 3360220213180203706L; 055 056 /** The outline paint. */ 057 private transient Paint outlinePaint; 058 059 /** The outline stroke. */ 060 private transient Stroke outlineStroke; 061 062 /** The interior space. */ 063 private RectangleInsets interiorGap; 064 065 /** The background paint. */ 066 private transient Paint backgroundPaint; 067 068 /** The shadow paint. */ 069 private transient Paint shadowPaint; 070 071 /** The shadow x-offset. */ 072 private double shadowXOffset = 2.0; 073 074 /** The shadow y-offset. */ 075 private double shadowYOffset = 2.0; 076 077 /** The text block. */ 078 private TextBlock textBlock; 079 080 /** 081 * Creates an empty text box. 082 */ 083 public TextBox() { 084 this((TextBlock) null); 085 } 086 087 /** 088 * Creates a text box. 089 * 090 * @param text the text. 091 */ 092 public TextBox(String text) { 093 this((TextBlock) null); 094 if (text != null) { 095 this.textBlock = new TextBlock(); 096 this.textBlock.addLine(text, new Font("SansSerif", Font.PLAIN, 10), 097 Color.BLACK); 098 } 099 } 100 101 /** 102 * Creates a new text box. 103 * 104 * @param block the text block. 105 */ 106 public TextBox(TextBlock block) { 107 this.outlinePaint = Color.BLACK; 108 this.outlineStroke = new BasicStroke(1.0f); 109 this.interiorGap = new RectangleInsets(1.0, 3.0, 1.0, 3.0); 110 this.backgroundPaint = new Color(255, 255, 192); 111 this.shadowPaint = Color.GRAY; 112 this.shadowXOffset = 2.0; 113 this.shadowYOffset = 2.0; 114 this.textBlock = block; 115 } 116 117 /** 118 * Returns the outline paint. 119 * 120 * @return The outline paint. 121 */ 122 public Paint getOutlinePaint() { 123 return this.outlinePaint; 124 } 125 126 /** 127 * Sets the outline paint. 128 * 129 * @param paint the paint. 130 */ 131 public void setOutlinePaint(Paint paint) { 132 this.outlinePaint = paint; 133 } 134 135 /** 136 * Returns the outline stroke. 137 * 138 * @return The outline stroke. 139 */ 140 public Stroke getOutlineStroke() { 141 return this.outlineStroke; 142 } 143 144 /** 145 * Sets the outline stroke. 146 * 147 * @param stroke the stroke. 148 */ 149 public void setOutlineStroke(Stroke stroke) { 150 this.outlineStroke = stroke; 151 } 152 153 /** 154 * Returns the interior gap. 155 * 156 * @return The interior gap. 157 */ 158 public RectangleInsets getInteriorGap() { 159 return this.interiorGap; 160 } 161 162 /** 163 * Sets the interior gap. 164 * 165 * @param gap the gap. 166 */ 167 public void setInteriorGap(RectangleInsets gap) { 168 this.interiorGap = gap; 169 } 170 171 /** 172 * Returns the background paint. 173 * 174 * @return The background paint. 175 */ 176 public Paint getBackgroundPaint() { 177 return this.backgroundPaint; 178 } 179 180 /** 181 * Sets the background paint. 182 * 183 * @param paint the paint. 184 */ 185 public void setBackgroundPaint(Paint paint) { 186 this.backgroundPaint = paint; 187 } 188 189 /** 190 * Returns the shadow paint. 191 * 192 * @return The shadow paint. 193 */ 194 public Paint getShadowPaint() { 195 return this.shadowPaint; 196 } 197 198 /** 199 * Sets the shadow paint. 200 * 201 * @param paint the paint. 202 */ 203 public void setShadowPaint(Paint paint) { 204 this.shadowPaint = paint; 205 } 206 207 /** 208 * Returns the x-offset for the shadow effect. 209 * 210 * @return The offset. 211 */ 212 public double getShadowXOffset() { 213 return this.shadowXOffset; 214 } 215 216 /** 217 * Sets the x-offset for the shadow effect. 218 * 219 * @param offset the offset (in Java2D units). 220 */ 221 public void setShadowXOffset(double offset) { 222 this.shadowXOffset = offset; 223 } 224 225 /** 226 * Returns the y-offset for the shadow effect. 227 * 228 * @return The offset. 229 */ 230 public double getShadowYOffset() { 231 return this.shadowYOffset; 232 } 233 234 /** 235 * Sets the y-offset for the shadow effect. 236 * 237 * @param offset the offset (in Java2D units). 238 */ 239 public void setShadowYOffset(double offset) { 240 this.shadowYOffset = offset; 241 } 242 243 /** 244 * Returns the text block. 245 * 246 * @return The text block. 247 */ 248 public TextBlock getTextBlock() { 249 return this.textBlock; 250 } 251 252 /** 253 * Sets the text block. 254 * 255 * @param block the block. 256 */ 257 public void setTextBlock(TextBlock block) { 258 this.textBlock = block; 259 } 260 261 /** 262 * Draws the text box. 263 * 264 * @param g2 the graphics device. 265 * @param x the x-coordinate. 266 * @param y the y-coordinate. 267 * @param anchor the anchor point. 268 */ 269 public void draw(Graphics2D g2, float x, float y, RectangleAnchor anchor) { 270 final Size2D d1 = this.textBlock.calculateDimensions(g2); 271 final double w = this.interiorGap.extendWidth(d1.getWidth()); 272 final double h = this.interiorGap.extendHeight(d1.getHeight()); 273 final Size2D d2 = new Size2D(w, h); 274 final Rectangle2D bounds 275 = RectangleAnchor.createRectangle(d2, x, y, anchor); 276 double xx = bounds.getX(); 277 double yy = bounds.getY(); 278 279 if (this.shadowPaint != null) { 280 final Rectangle2D shadow = new Rectangle2D.Double( 281 xx + this.shadowXOffset, yy + this.shadowYOffset, 282 bounds.getWidth(), bounds.getHeight()); 283 g2.setPaint(this.shadowPaint); 284 g2.fill(shadow); 285 } 286 if (this.backgroundPaint != null) { 287 g2.setPaint(this.backgroundPaint); 288 g2.fill(bounds); 289 } 290 291 if (this.outlinePaint != null && this.outlineStroke != null) { 292 g2.setPaint(this.outlinePaint); 293 g2.setStroke(this.outlineStroke); 294 g2.draw(bounds); 295 } 296 297 this.textBlock.draw(g2, 298 (float) (xx + this.interiorGap.calculateLeftInset(w)), 299 (float) (yy + this.interiorGap.calculateTopInset(h)), 300 TextBlockAnchor.TOP_LEFT); 301 302 } 303 304 /** 305 * Returns the height of the text box. 306 * 307 * @param g2 the graphics device. 308 * 309 * @return The height (in Java2D units). 310 */ 311 public double getHeight(Graphics2D g2) { 312 final Size2D d = this.textBlock.calculateDimensions(g2); 313 return this.interiorGap.extendHeight(d.getHeight()); 314 } 315 316 /** 317 * Tests this object for equality with an arbitrary object. 318 * 319 * @param obj the object to test against ({@code null} permitted). 320 * 321 * @return A boolean. 322 */ 323 @Override 324 public boolean equals(Object obj) { 325 if (obj == this) { 326 return true; 327 } 328 if (!(obj instanceof TextBox)) { 329 return false; 330 } 331 final TextBox that = (TextBox) obj; 332 if (!Objects.equals(this.outlinePaint, that.outlinePaint)) { 333 return false; 334 } 335 if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { 336 return false; 337 } 338 if (!Objects.equals(this.interiorGap, that.interiorGap)) { 339 return false; 340 } 341 if (!Objects.equals(this.backgroundPaint, that.backgroundPaint)) { 342 return false; 343 } 344 if (!Objects.equals(this.shadowPaint, that.shadowPaint)) { 345 return false; 346 } 347 if (this.shadowXOffset != that.shadowXOffset) { 348 return false; 349 } 350 if (this.shadowYOffset != that.shadowYOffset) { 351 return false; 352 } 353 if (!Objects.equals(this.textBlock, that.textBlock)) { 354 return false; 355 } 356 357 return true; 358 } 359 360 /** 361 * Returns a hash code for this object. 362 * 363 * @return A hash code. 364 */ 365 @Override 366 public int hashCode() { 367 int result; 368 long temp; 369 result = (this.outlinePaint != null ? this.outlinePaint.hashCode() : 0); 370 result = 29 * result + (this.outlineStroke != null 371 ? this.outlineStroke.hashCode() : 0); 372 result = 29 * result + (this.interiorGap != null 373 ? this.interiorGap.hashCode() : 0); 374 result = 29 * result + (this.backgroundPaint != null 375 ? this.backgroundPaint.hashCode() : 0); 376 result = 29 * result + (this.shadowPaint != null 377 ? this.shadowPaint.hashCode() : 0); 378 temp = this.shadowXOffset != +0.0d 379 ? Double.doubleToLongBits(this.shadowXOffset) : 0L; 380 result = 29 * result + (int) (temp ^ (temp >>> 32)); 381 temp = this.shadowYOffset != +0.0d 382 ? Double.doubleToLongBits(this.shadowYOffset) : 0L; 383 result = 29 * result + (int) (temp ^ (temp >>> 32)); 384 result = 29 * result + (this.textBlock != null 385 ? this.textBlock.hashCode() : 0); 386 return result; 387 } 388 389 /** 390 * Provides serialization support. 391 * 392 * @param stream the output stream. 393 * 394 * @throws IOException if there is an I/O error. 395 */ 396 private void writeObject(ObjectOutputStream stream) throws IOException { 397 stream.defaultWriteObject(); 398 SerialUtils.writePaint(this.outlinePaint, stream); 399 SerialUtils.writeStroke(this.outlineStroke, stream); 400 SerialUtils.writePaint(this.backgroundPaint, stream); 401 SerialUtils.writePaint(this.shadowPaint, stream); 402 } 403 404 /** 405 * Provides serialization support. 406 * 407 * @param stream the input stream. 408 * 409 * @throws IOException if there is an I/O error. 410 * @throws ClassNotFoundException if there is a classpath problem. 411 */ 412 private void readObject(ObjectInputStream stream) throws IOException, 413 ClassNotFoundException { 414 stream.defaultReadObject(); 415 this.outlinePaint = SerialUtils.readPaint(stream); 416 this.outlineStroke = SerialUtils.readStroke(stream); 417 this.backgroundPaint = SerialUtils.readPaint(stream); 418 this.shadowPaint = SerialUtils.readPaint(stream); 419 } 420 421} 422