001/*
002 * $Id: RectanglePainter.java 4147 2012-02-01 17:13:24Z 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.BasicStroke;
025import java.awt.Color;
026import java.awt.Graphics2D;
027import java.awt.Insets;
028import java.awt.Paint;
029import java.awt.Rectangle;
030import java.awt.Shape;
031import java.awt.geom.Rectangle2D;
032import java.awt.geom.RectangularShape;
033import java.awt.geom.RoundRectangle2D;
034
035import org.jdesktop.beans.JavaBean;
036import org.jdesktop.swingx.painter.effects.AreaEffect;
037import org.jdesktop.swingx.util.ShapeUtils;
038
039/**
040 * A painter which paints square and rounded rectangles
041 * 
042 * @author joshua.marinacci@sun.com
043 */
044@JavaBean
045@SuppressWarnings("nls")
046public class RectanglePainter extends AbstractAreaPainter<Object> {
047    private boolean rounded = false;
048    //private Insets insets = new Insets(0,0,0,0);
049    private int roundWidth = 20;
050    private int roundHeight = 20;
051    private int width = -1;
052    private int height = -1;
053    //private double strokeWidth = 1;
054
055    /** Creates a new instance of RectanglePainter */
056    public RectanglePainter() {
057        this(0, 0, 0, 0);
058    }
059    
060    public RectanglePainter(int top, int left, int bottom, int right) {
061        this(top, left, bottom, right, 0, 0);
062    }
063
064    public RectanglePainter(int top, int left, int bottom, int right,
065            int roundWidth, int roundHeight) {
066        this(top, left, bottom, right, roundWidth, roundHeight, roundWidth != 0 || roundHeight != 0, Color.RED, 1f, Color.BLACK);
067    }
068
069    public RectanglePainter(int top, int left, int bottom, int right, int roundWidth,
070            int roundHeight, boolean rounded, Paint fillPaint, float strokeWidth, Paint borderPaint) {
071        this(new Insets(top, left, bottom, right), -1, -1, roundWidth, roundHeight, rounded, fillPaint, strokeWidth, borderPaint);
072    }
073
074    public RectanglePainter(Color fillPaint, Color borderPaint) {
075        this(fillPaint, borderPaint, 1f, null);
076    }
077
078    public RectanglePainter(Paint fillPaint, Paint borderPaint, float borderWidth, Style style) {
079        this(new Insets(0, 0, 0, 0), -1, -1, 0, 0, false, fillPaint, borderWidth, borderPaint);
080        setStyle(style);
081        setDirty(false);
082    }
083
084    public RectanglePainter(int width, int height, int cornerRadius, Paint fillPaint) {
085        this(new Insets(0, 0, 0, 0), width, height, cornerRadius, cornerRadius, true, fillPaint, 1f, Color.BLACK);
086    }
087
088    public RectanglePainter(Insets insets, int width, int height, int roundWidth, int roundHeight,
089            boolean rounded, Paint fillPaint, float strokeWidth, Paint borderPaint) {
090        this.width = width;
091        this.height = height;
092        setFillHorizontal(width < 0);
093        setFillVertical(height < 0);
094        setInsets(insets);
095        this.roundWidth = roundWidth;
096        this.roundHeight = roundHeight;
097        this.rounded = rounded;
098        this.setFillPaint(fillPaint);
099        this.setBorderWidth(strokeWidth);
100        this.setBorderPaint(borderPaint);
101        this.setDirty(false);
102    }
103
104    /**
105     * Indicates if the rectangle is rounded
106     * @return if the rectangle is rounded
107     */
108    public boolean isRounded() {
109        return rounded;
110    }
111
112    /**
113     * sets if the rectangle should be rounded
114     * @param rounded if the rectangle should be rounded
115     */
116    public void setRounded(boolean rounded) {
117        boolean oldRounded = isRounded();
118        this.rounded = rounded;
119        setDirty(true);
120        firePropertyChange("rounded",oldRounded,rounded);
121    }
122
123    /**
124     * gets the round width of the rectangle
125     * @return the current round width
126     */
127    public int getRoundWidth() {
128        return roundWidth;
129    }
130
131    /**
132     * sets the round width of the rectangle
133     * @param roundWidth a new round width
134     */
135    public void setRoundWidth(int roundWidth) {
136        int oldRoundWidth = getRoundWidth();
137        this.roundWidth = roundWidth;
138        setDirty(true);
139        firePropertyChange("roundWidth",oldRoundWidth,roundWidth);
140    }
141
142    /**
143     * gets the round height of the rectangle
144     * @return the current round height
145     */
146    public int getRoundHeight() {
147        return roundHeight;
148    }
149
150    /**
151     * sets the round height of the rectangle
152     * @param roundHeight a new round height
153     */
154    public void setRoundHeight(int roundHeight) {
155        int oldRoundHeight = getRoundHeight();
156        this.roundHeight = roundHeight;
157        setDirty(true);
158        firePropertyChange("roundHeight",oldRoundHeight,roundHeight);
159    }
160
161
162    /* ======== drawing code ============ */
163    protected RectangularShape calculateShape(int width, int height) {
164        Insets insets = getInsets();
165        int x = insets.left;
166        int y = insets.top;
167
168        // use the position calcs from the super class
169        Rectangle bounds = calculateLayout(this.width, this.height, width, height);
170        if(this.width != -1 && !isFillHorizontal()) {
171            width = this.width;
172            x = bounds.x;
173        }
174        if(this.height != -1 && !isFillVertical()) {
175            height = this.height;
176            y = bounds.y;
177        }
178
179        if(isFillHorizontal()) {
180            width = width - insets.left - insets.right;
181        }
182        if(isFillVertical()) {
183            height = height - insets.top - insets.bottom;
184        }
185
186
187        RectangularShape shape = new Rectangle2D.Double(x, y, width, height);
188        if(rounded) {
189            shape = new RoundRectangle2D.Double(x, y, width, height, roundWidth, roundHeight);
190        }
191        return shape;
192    }
193
194
195
196    @Override
197    protected void doPaint(Graphics2D g, Object component, int width, int height) {
198        Shape shape = provideShape(g, component, width, height);
199        switch (getStyle()) {
200        case BOTH:
201            drawBackground(g,shape,width,height);
202            drawBorder(g,shape,width,height);
203            break;
204        case FILLED:
205            drawBackground(g,shape,width,height);
206            break;
207        case OUTLINE:
208            drawBorder(g,shape,width,height);
209            break;
210        }
211
212        // background
213        // border
214        // leave the clip to support masking other painters
215        ShapeUtils.mergeClip(g,shape);
216        /*
217        Area area = new Area(g.getClip());
218        area.intersect(new Area(shape));//new Rectangle(0,0,width,height)));
219        g.setClip(area);*/
220        //g.setClip(shape);
221    }
222
223    private void drawBorder(Graphics2D g, Shape shape, int width, int height) {
224        Paint p = getBorderPaint();
225        if(isPaintStretched()) {
226            p = calculateSnappedPaint(p, width, height);
227        }
228
229        g.setPaint(p);
230
231        g.setStroke(new BasicStroke(getBorderWidth()));
232        
233        // shrink the border by 1 px
234        if(shape instanceof Rectangle2D) {
235            Rectangle2D rect = (Rectangle2D) shape;
236            
237            g.draw(new Rectangle2D.Double(rect.getX(), rect.getY(),
238                    rect.getWidth()-1, rect.getHeight()-1));
239        } else if(shape instanceof RoundRectangle2D) {
240            RoundRectangle2D rect = (RoundRectangle2D) shape;
241            
242            g.draw(new RoundRectangle2D.Double(rect.getX(), rect.getY(),
243                    rect.getWidth()-1, rect.getHeight()-1,
244                    rect.getArcWidth(), rect.getArcHeight()));
245        } else {
246            g.draw(shape);
247        }
248    }
249
250    private void drawBackground(Graphics2D g, Shape shape, int width, int height) {
251        Paint p = getFillPaint();
252        if(isPaintStretched()) {
253            p = calculateSnappedPaint(p, width, height);
254        }
255
256        g.setPaint(p);
257
258        g.fill(shape);
259        if(getAreaEffects() != null) {
260            for(AreaEffect ef : getAreaEffects()) {
261                ef.apply(g, shape, width, height);
262            }
263        }
264    }
265
266    @Override
267    protected Shape provideShape(Graphics2D g, Object comp, int width, int height) {
268        return calculateShape(width,height);
269    }
270}
271