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