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