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