001    /*
002     * $Id: AbstractAreaPainter.java 2476 2007-11-25 15:52:59Z kschaefe $
003     *
004     * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     *
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx.painter;
023    
024    import org.jdesktop.swingx.painter.effects.AreaEffect;
025    
026    import java.awt.*;
027    import org.jdesktop.swingx.util.PaintUtils;
028    
029    
030    /**
031     * The abstract base class for all painters that fill a vector path area.  This
032     * includes Shapes, Rectangles, Text, and the MattePainter
033     * which fills in the entire background of a component.  The defining
034     * feature of AbstractAreaPainter subclasses
035     * is that they implement the provideShape() method which returns
036     * the outline shape of the area that this
037     * painter will fill. Subclasses must implement the provideShape() method.
038     *
039     * The AbstractAreaPainter provides support for the following common painting properties
040     *
041     * <ul>
042     * <li>fillPaint</li>
043     * <li>paintStretched</li>
044     * <li>borderPaint</li>
045     * <li>borderWidth</li>
046     * <li>style</li>
047     * </ul>
048     *
049     * The AbstractAreaPainter also provides support for path effects like dropshadows and glows.
050     *
051     * @author joshua@marinacci.org
052     */
053    public abstract class AbstractAreaPainter<T> extends AbstractLayoutPainter<T> {
054        
055        /**
056         * Different available fill styles. BOTH indicates that both the outline,
057         * and the fill should be painted. This is the default. FILLED indicates that
058         * the shape should be filled, but no outline painted. OUTLINE specifies that
059         * the shape should be outlined, but not filled. NONE indicates that neither
060         * the fill area nor the outline should be painted.
061         */
062        public enum Style {BOTH, FILLED, OUTLINE, NONE}
063        
064        // controls if the paint should be stretched to fill the available area
065        private boolean stretchPaint;
066        
067        private AreaEffect[] areaEffects = new AreaEffect[0];
068        
069        
070        private Style style = Style.BOTH;
071        /**
072         * The stroke width to use when painting. If null, the default Stroke for
073         * the Graphics2D is used
074         */
075        private float borderWidth;
076        
077        /**
078         * The paint to use when filling the shape
079         */
080        private Paint fillPaint;
081        
082        /**
083         * The Paint to use when stroking the shape (drawing the outline). If null,
084         * then the component foreground color is used
085         */
086        private Paint borderPaint;
087        
088        /**
089         * Creates a new instance of AbstractAreaPainter
090         */
091        public AbstractAreaPainter() {
092            fillPaint = Color.RED;
093        }
094        /**
095         * Creates a new instance of AbstractAreaPainter
096         * @param paint the default paint to fill this area painter with
097         */
098        public AbstractAreaPainter(Paint paint) {
099            this.fillPaint = paint;
100        }
101        
102        
103        /**
104         * Gets the current fill paint. This is the Paint object that will be used to fill the path area.
105         * @return Gets the Paint being used. May be null
106         */
107        public Paint getFillPaint() {
108            return fillPaint;
109        }
110        
111        /**
112         * Sets the Paint to use. This is the Paint object that will be used to fill the path area. If null, nothing is painted
113         * @param p the Paint to use
114         */
115        public void setFillPaint(Paint p) {
116            Paint old = getFillPaint();
117            this.fillPaint = p;
118            setDirty(true);
119            firePropertyChange("paint", old, getFillPaint());
120        }
121        
122        /**
123         * Indicates if the paint will be snapped. This means that the paint will be scaled and aligned along the 4 axis of (horizontal, vertical,
124         * and both diagonals). Snapping allows the paint to be stretched across the component when it is drawn, even if the component is
125         * resized. This setting is only used for gradient paints. It will have no effect on Color or Texture paints.
126         * @return the current value of the snapPaint property
127         */
128        public boolean isPaintStretched() {
129            return stretchPaint;
130        }
131        
132        
133        /**
134         * Specifies whether this Painter should attempt to resize the Paint to fit the area being painted.
135         * For example, if true, then a gradient specified as (0, 0), (1, 0) would stretch horizontally such that
136         * the beginning of the gradient is on the left edge of the painted region, and the end of the gradient
137         * is at the right edge of the painted region.
138         * Specifically, if true, the resizePaint method will be called to perform the actual resizing of the Paint
139         * @param paintStretched true if the paint should be stretched, false otherwise.
140         */
141        public void setPaintStretched(boolean paintStretched) {
142            boolean old = this.isPaintStretched();
143            this.stretchPaint = paintStretched;
144            setDirty(true);
145            firePropertyChange("snapPaint",old,this.stretchPaint);
146        }
147        
148        /**
149         * The Paint to use for stroking the shape (painting the outline).
150         * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint.
151         * If null, the component foreground is used.
152         *
153         * @param p the Paint to use for stroking the shape. May be null.
154         */
155        public void setBorderPaint(Paint p) {
156            Paint old = getBorderPaint();
157            this.borderPaint = p;
158            setDirty(true);
159            firePropertyChange("borderPaint", old, getBorderPaint());
160        }
161        
162        /**
163         * Gets the current Paint to use for stroking the shape (painting the outline).
164         * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint.
165         * If null, the component foreground is used.
166         * @return the Paint used when stroking the shape. May be null
167         */
168        public Paint getBorderPaint() {
169            return borderPaint;
170        }
171        
172        /**
173         * The shape can be filled or simply stroked (outlined), or both or none. By default,
174         * the shape is both filled and stroked. This property specifies the strategy to
175         * use.
176         * @param s the Style to use. If null, Style.BOTH is used
177         */
178        public void setStyle(Style s) {
179            Style old = getStyle();
180            this.style = s == null ? Style.BOTH : s;
181            setDirty(true);
182            firePropertyChange("style", old, getStyle());
183        }
184        
185        /**
186         * Gets the current Style. The shape can be filled or simply stroked (outlined), or both or none. By default,
187         * the shape is both filled and stroked. This property specifies the strategy to
188         * use.
189         * @return the Style used
190         */
191        public Style getStyle() {
192            return style;
193        }
194        
195        /**
196         * Sets the border width to use for painting. If null, then the default Graphics2D
197         * stroke will be used.  The stroke will be centered on the actual shape outline.
198         * @param s the Stroke to fillPaint with
199         */
200        public void setBorderWidth(float s) {
201            float old = getBorderWidth();
202            this.borderWidth = s;
203            setDirty(true);
204            firePropertyChange("strokeWidth", old, getBorderWidth());
205        }
206        
207        /**
208         * Gets the current border width.
209         * @return the Stroke to use for painting
210         */
211        public float getBorderWidth() {
212            return borderWidth;
213        }
214        
215        
216        
217        /**
218         * Resizes the given Paint. By default, only Gradients, LinearGradients, and RadialGradients are resized
219         * in this method. If you have special resizing needs, override this method. This
220         * method is mainly used to make gradient paints resize with the component this
221         * painter is attached to. This method is internal to the painter api and should
222         * not be called elsewhere. It is used by the paintStretched property and
223         * painter subclasses. In the future it may be made public for use by other classes.
224         * If this happens it should probably be turned into a static utility method.
225         */
226        Paint calculateSnappedPaint(Paint p, int width, int height) {
227            return PaintUtils.resizeGradient(p, width, height);
228        }
229        
230        
231        
232        /**
233         * Returns the outline shape of this painter. Subclasses must implement this method. This shape
234         * will be used for filling, stroking, and clipping.
235         * @return the outline shape of this painter
236         * @param g graphics
237         * @param comp The Object this painter will be painted on.
238         * @param width the width to paint
239         * @param height the height to paint
240         */
241        protected abstract Shape provideShape(Graphics2D g, T comp, int width, int height);
242        
243        /**
244         * Sets the path effects to be drawn on this painter. Set this to null in order to remove all installed effects.
245         * @param areaEffects the effects to apply to this painter
246         */
247        public void setAreaEffects(AreaEffect... areaEffects) {
248            AreaEffect[] old = getAreaEffects();
249            this.areaEffects = new AreaEffect[areaEffects == null ? 0 : areaEffects.length];
250            if (areaEffects != null) {
251                System.arraycopy(areaEffects, 0, this.areaEffects, 0, this.areaEffects.length);
252            }
253            setDirty(true);
254            firePropertyChange("areaEffects", old, getAreaEffects());
255        }
256        
257        /**
258         * Gets the current set of path effects applied to this painter. Returned array is guarantied to be not null.
259         * @return the effects applied to this path painter
260         */
261        public AreaEffect[] getAreaEffects() {
262            AreaEffect[] results = new AreaEffect[areaEffects.length];
263            System.arraycopy(areaEffects, 0, results, 0, results.length);
264            return results;
265        }
266        
267    }