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    
048    package org.jfree.text;
049    
050    import java.awt.BasicStroke;
051    import java.awt.Color;
052    import java.awt.Font;
053    import java.awt.Graphics2D;
054    import java.awt.Paint;
055    import java.awt.Stroke;
056    import java.awt.geom.Rectangle2D;
057    import java.io.IOException;
058    import java.io.ObjectInputStream;
059    import java.io.ObjectOutputStream;
060    import java.io.Serializable;
061    
062    import org.jfree.io.SerialUtilities;
063    import org.jfree.ui.RectangleAnchor;
064    import org.jfree.ui.RectangleInsets;
065    import org.jfree.ui.Size2D;
066    import org.jfree.util.ObjectUtilities;
067    
068    /**
069     * A box containing a text block.
070     *
071     * @author David Gilbert
072     */
073    public 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    }