001/*
002 * $Id: ShapePainter.java 4147 2012-02-01 17:13:24Z kschaefe $
003 *
004 * Copyright 2004 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 static org.jdesktop.swingx.painter.PainterUtils.getBackgroundPaint;
025import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint;
026
027import java.awt.BasicStroke;
028import java.awt.Color;
029import java.awt.Graphics2D;
030import java.awt.Paint;
031import java.awt.Rectangle;
032import java.awt.Shape;
033import java.awt.geom.Ellipse2D;
034
035import org.jdesktop.beans.JavaBean;
036import org.jdesktop.swingx.painter.effects.AreaEffect;
037
038
039/**
040 * <p>A Painter that paints java.awt.Shapes. It uses a stroke and a fillPaint to do so. The
041 * shape is painted as is, at a specific location. If no Shape is specified, nothing
042 * will be painted. If no stroke is specified, the default for the Graphics2D
043 * will be used. If no fillPaint is specified, the component background color
044 * will be used. The shape can be positioned using the insets, horizontal, and
045 * vertical properties.</p>
046 * 
047 * <p>Here is an example that draws a rectangle aligned on the center right:
048 * <pre><code>
049 *  Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, 50, 50);
050 *  ShapePainter p = new ShapePainter(rect);
051 * p.setHorizontal(HorizontalAlignment.RIGHT);
052 * p.setVertical(VerticalAlignment.CENTER);
053 * </code></pre>
054 * @author rbair
055 */
056@JavaBean
057@SuppressWarnings("nls")
058public class ShapePainter extends AbstractAreaPainter<Object> {
059    /**
060     * The Shape to fillPaint. If null, nothing is painted.
061     */
062    private Shape shape;
063    
064    /**
065     * Create a new ShapePainter
066     */
067    public ShapePainter() {
068        super();
069        this.shape = new Ellipse2D.Double(0,0,100,100);
070        this.setBorderWidth(3);
071        this.setFillPaint(Color.RED);
072        this.setBorderPaint(Color.BLACK);
073    }
074    
075    /**
076     * Create a new ShapePainter with the specified shape.
077     *
078     *
079     * @param shape the shape to fillPaint
080     */
081    public ShapePainter(Shape shape) {
082        super();
083        this.shape = shape;
084    }
085    
086    /**
087     * Create a new ShapePainter with the specified shape and fillPaint.
088     *
089     *
090     * @param shape the shape to fillPaint
091     * @param paint the fillPaint to be used to fillPaint the shape
092     */
093    public ShapePainter(Shape shape, Paint paint) {
094        super();
095        this.shape = shape;
096        this.setFillPaint(paint);
097    }
098    
099    /**
100     * Create a new ShapePainter with the specified shape and fillPaint. The shape
101     * can be filled or stroked (only the outline is painted).
102     *
103     *
104     * @param shape the shape to fillPaint
105     * @param paint the fillPaint to be used to fillPaint the shape
106     * @param style specifies the ShapePainter.Style to use for painting this shape.
107     *        If null, then Style.BOTH is used
108     */
109    public ShapePainter(Shape shape, Paint paint, Style style) {
110        super();
111        this.shape = shape;
112        this.setFillPaint(paint);
113        this.setStyle(style == null ? Style.BOTH : style);
114    }
115    
116    /**
117     * Sets the shape to fillPaint. This shape is not resized when the component
118     * bounds are. To do that, create a custom shape that is bound to the
119     * component width/height
120     *
121     *
122     * @param s the Shape to fillPaint. May be null
123     */
124    public void setShape(Shape s) {
125        Shape old = getShape();
126        this.shape = s;
127        setDirty(true);
128        firePropertyChange("shape", old, getShape());
129    }
130    
131    /**
132     * Gets the current shape
133     * @return the Shape to fillPaint. May be null
134     */
135    public Shape getShape() {
136        return shape;
137    }
138    
139    /**
140     * {@inheritDoc}
141     */
142    @Override
143    protected void doPaint(Graphics2D g, Object component, int w, int h) {
144        g.setStroke(new BasicStroke(this.getBorderWidth()));
145        
146        if(getShape() != null) {
147            Shape s = provideShape(g,component, w, h);
148            Rectangle bounds = s.getBounds();
149            Rectangle rect = calculateLayout(bounds.width, bounds.height, w, h);
150            //u.p("rect = " + rect);
151            g = (Graphics2D)g.create();
152            
153            try {
154                g.translate(rect.x, rect.y);
155                //draw/fill the shape
156                drawPathEffects(g, s, rect.width, rect.height);
157                switch (getStyle()) {
158                    case BOTH:
159                        drawShape(g, s, component, rect.width, rect.height);
160                        fillShape(g, s, component, rect.width, rect.height);
161                        break;
162                    case FILLED:
163                        fillShape(g, s, component, rect.width, rect.height);
164                        break;
165                    case OUTLINE:
166                        drawShape(g, s, component, rect.width, rect.height);
167                        break;
168                }
169            } finally {
170                g.dispose();
171        }
172    }
173    }
174    
175    private void drawShape(Graphics2D g, Shape s, Object component, int w, int h) {
176        g.setPaint(calculateStrokePaint(component, w, h));
177        g.draw(s);
178    }
179    
180    private void fillShape(Graphics2D g, Shape s, Object component, int w, int h) {
181        g.setPaint(calculateFillPaint(component, w, h));
182        g.fill(s);
183    }
184    
185    // shape effect stuff
186    @Override
187    protected Shape provideShape(Graphics2D g, Object comp, int width, int height) {
188        return getShape();
189    }
190    
191    private Paint calculateStrokePaint(Object component, int width, int height) {
192        Paint p = getForegroundPaint(getBorderPaint(), component);
193        if(isPaintStretched()) {
194            p = calculateSnappedPaint(p, width, height);
195        }
196        return p;
197    }
198    
199    private Paint calculateFillPaint(Object component, int width, int height) {
200        //set the fillPaint
201        Paint p = getBackgroundPaint(getFillPaint(), component);
202        if(isPaintStretched()) {
203            p = calculateSnappedPaint(p, width, height);
204        }
205        return p;
206    }
207
208    private void drawPathEffects(Graphics2D g, Shape s, int w, int h) {
209        if(getAreaEffects() != null) {
210            //Paint pt = calculateFillPaint(component, w, h);
211            for(AreaEffect ef : getAreaEffects()) {
212                ef.apply(g, s, w, h);
213            }
214        }
215    }
216}