001/*
002 * $Id: PinstripePainter.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 java.awt.BasicStroke;
025import java.awt.Graphics2D;
026import java.awt.Paint;
027import java.awt.Rectangle;
028import java.awt.Shape;
029import java.awt.geom.Area;
030import java.awt.geom.Line2D;
031
032import org.jdesktop.beans.JavaBean;
033
034/**
035 * <p>A fun Painter that paints pinstripes. You can specify the Paint to paint
036 * those pinstripes in (could even be a texture paint!), the angle at which
037 * to paint the pinstripes, and the spacing between stripes.</p>
038 *
039 * <p>The default PinstripePainter configuration will paint the pinstripes
040 * using the foreground color of the component (the default behavior if a
041 * Paint is not specified) at a 45 degree angle with 8 pixels between stripes</p>
042 *
043 * <p>Here is a custom code snippet that paints Color.GRAY pinstripes at a 135
044 * degree angle:
045 * <pre><code>
046 *  PinstripePainter p = new PinstripePainter();
047 *  p.setAngle(135);
048 *  p.setPaint(Color.GRAY);
049 * </code></pre>
050 *
051 * @author rbair
052 */
053@JavaBean
054public class PinstripePainter extends AbstractPainter<Object> {
055    /**
056     * The angle in degrees to paint the pinstripes at. The default
057     * value is 45. The value will be between 0 and 360 inclusive. The
058     * setAngle method will ensure this.
059     */
060    private double angle = 45;
061    /**
062     * The spacing between pinstripes
063     */
064    private double spacing = 8;
065    
066    /**
067     * The stroke width of the pinstripes
068     */
069    private double stripeWidth = 1;
070    
071    /**
072     * The Paint to use when drawing the pinstripes
073     */
074    private Paint paint;
075    
076    /**
077     * Create a new PinstripePainter. By default the angle with be 45 degrees,
078     * the spacing will be 8 pixels, and the color will be the Component foreground
079     * color.
080     */
081    public PinstripePainter() {
082    }
083    
084    /**
085     * Create a new PinstripePainter using an angle of 45, 8 pixel spacing,
086     * and the given Paint.
087     *
088     * @param paint the paint used when drawing the stripes
089     */
090    public PinstripePainter(Paint paint) {
091        this(paint, 45);
092    }
093    
094    /**
095     * Create a new PinstripePainter using the given angle, 8 pixel spacing,
096     * and the given Paint
097     *
098     * @param paint the paint used when drawing the stripes
099     * @param angle the angle, in degrees, in which to paint the pinstripes
100     */
101    public PinstripePainter(Paint paint, double angle) {
102        this.paint = paint;
103        this.angle = angle;
104    }
105    
106    /**
107     * Create a new PinstripePainter using the given angle, 8 pixel spacing,
108     * and the foreground color of the Component
109     *
110     * @param angle the angle, in degrees, in which to paint the pinstripes
111     */
112    public PinstripePainter(double angle) {
113        this.angle = angle;
114    }
115    
116    /**
117     * Create a new PinstripePainter with the specified paint, angle, stripe width, and stripe spacing.
118     * @param paint 
119     * @param angle 
120     * @param stripeWidth 
121     * @param spacing 
122     */
123    public PinstripePainter(Paint paint, double angle, double stripeWidth, double spacing) {
124        this.paint = paint;
125        this.angle = angle;
126        this.stripeWidth = stripeWidth;
127        this.spacing = spacing;
128    }
129    
130    /**
131     * Set the paint to use for drawing the pinstripes
132     *
133     * @param p the Paint to use. May be a Color.
134     */
135    public void setPaint(Paint p) {
136        Paint old = getPaint();
137        this.paint = p;
138        firePropertyChange("paint", old, getPaint());
139    }
140    
141    /**
142     * Get the current paint used for drawing the pinstripes
143     * @return the Paint to use to draw the pinstripes
144     */
145    public Paint getPaint() {
146        return paint;
147    }
148    
149    /**
150     * Sets the angle, in degrees, at which to paint the pinstripes. If the
151     * given angle is < 0 or > 360, it will be appropriately constrained. For
152     * example, if a value of 365 is given, it will result in 5 degrees. The
153     * conversion is not perfect, but "a man on a galloping horse won't be
154     * able to tell the difference".
155     *
156     * @param angle the Angle in degrees at which to paint the pinstripes
157     */
158    public void setAngle(double angle) {
159        if (angle > 360) {
160            angle = angle % 360;
161        }
162        
163        if (angle < 0) {
164            angle = 360 - ((angle * -1) % 360);
165        }
166        
167        double old = getAngle();
168        this.angle = angle;
169        firePropertyChange("angle", old, getAngle());
170    }
171    
172    /**
173     * Gets the current angle of the pinstripes
174     * @return the angle, in degrees, at which the pinstripes are painted
175     */
176    public double getAngle() {
177        return angle;
178    }
179    
180    /**
181     * Sets the spacing between pinstripes
182     *
183     * @param spacing spacing between pinstripes
184     */
185    public void setSpacing(double spacing) {
186        double old = getSpacing();
187        this.spacing = spacing;
188        firePropertyChange("spacing", old, getSpacing());
189    }
190    
191    /**
192     * Get the current spacing between the stripes
193     * @return the spacing between pinstripes
194     */
195    public double getSpacing() {
196        return spacing;
197    }
198    
199    /**
200     * {@inheritDoc}
201     */
202    @Override
203    protected void doPaint(Graphics2D g, Object component, int width, int height) {
204        //draws pinstripes at the angle specified in this class
205        //and at the given distance apart
206        Shape oldClip = g.getClip();
207        Area area = new Area(new Rectangle(0,0,width,height));
208        if(oldClip != null) {
209            area = new Area(oldClip);
210        }
211        area.intersect(new Area(new Rectangle(0,0,width,height)));
212        g.setClip(area);
213        //g.setClip(oldClip.intersection(new Rectangle(0,0,width,height)));
214        Paint p = PainterUtils.getForegroundPaint(getPaint(), component);
215        g.setPaint(p);
216        
217        g.setStroke(new BasicStroke((float)getStripeWidth()));
218        
219        double hypLength = Math.sqrt((width * width) +
220                (height * height));
221        
222        double radians = Math.toRadians(getAngle());
223        g.rotate(radians);
224        
225        double spacing = getSpacing();
226        spacing += getStripeWidth();
227        int numLines = (int)(hypLength / spacing);
228        
229        for (int i=0; i<numLines; i++) {
230            double x = i * spacing;
231            Line2D line = new Line2D.Double(x, -hypLength, x, hypLength);
232            g.draw(line);
233        }
234        g.setClip(oldClip);
235    }
236    
237    /**
238     * Gets the current width of the pinstripes
239     * @return the current pinstripe width
240     */
241    public double getStripeWidth() {
242        return stripeWidth;
243    }
244    
245    /**
246     * Set the width of the pinstripes
247     * @param stripeWidth a new width for the pinstripes
248     */
249    public void setStripeWidth(double stripeWidth) {
250        double oldSripeWidth = getStripeWidth();
251        this.stripeWidth = stripeWidth;
252        firePropertyChange("stripeWidth",new Double(oldSripeWidth),new Double(stripeWidth));
253    }
254    
255}