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