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