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