001: /*
002: * $Id: ShapePainter.java,v 1.11 2006/05/14 15:55:54 dmouse Exp $
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.Graphics2D;
025: import java.awt.Paint;
026: import java.awt.Shape;
027: import java.awt.Stroke;
028: import java.awt.geom.AffineTransform;
029: import java.awt.geom.Point2D;
030: import java.awt.geom.Rectangle2D;
031: import java.awt.geom.RoundRectangle2D;
032: import javax.swing.JComponent;
033: import org.jdesktop.swingx.util.Resize;
034:
035: /**
036: * <p>A Painter that paints Shapes. It uses a stroke and a fillPaint to do so. The
037: * shape is painted as is, at a specific location. If no Shape is specified, nothing
038: * will be painted. If no stroke is specified, the default for the Graphics2D
039: * will be used. If no fillPaint is specified, the component background color
040: * will be used. And if no location is specified, then the shape will be draw
041: * at the origin (0,0)</p>
042: *
043: * <p>Here is an example that draws a lowly rectangle:
044: * <pre><code>
045: * Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, 50, 50);
046: * ShapePainter p = new ShapePainter(rect);
047: * p.setLocation(new Point2D.Double(20, 10));
048: * </code></pre>
049: *
050: *
051: * @author rbair
052: */
053: public class ShapePainter extends AbstractPainter {
054: /**
055: * Different available fill styles. BOTH indicates that both the outline,
056: * and the fill should be painted. This is the default. FILLED indicates that
057: * the shape should be filled, but no outline painted. OUTLINE specifies that
058: * the shape should be outlined, but not filled
059: */
060: public enum Style {
061: BOTH, FILLED, OUTLINE
062: }
063:
064: /**
065: * The Shape to fillPaint. If null, nothing is painted.
066: */
067: private Shape shape;
068: /**
069: * The Stroke to use when painting. If null, the default Stroke for
070: * the Graphics2D is used
071: */
072: private Stroke stroke;
073: /**
074: * The Paint to use when painting the shape. If null, then the component
075: * background color is used
076: */
077: private Paint fillPaint;
078: /**
079: * The Paint to use when stroking the shape (drawing the outline). If null,
080: * then the component foreground color is used
081: */
082: private Paint strokePaint;
083: /**
084: * The location at which to draw the shape. If null, 0,0 is used
085: */
086: private Point2D location = new Point2D.Double(0, 0);
087: /**
088: * Specifies if/how resizing (relocating) the location should occur.
089: */
090: private Resize resizeLocation = Resize.BOTH;
091: /**
092: * Specifies if/how resizing of the shape should occur
093: */
094: private Resize resize = Resize.BOTH;
095: /**
096: * Indicates whether the shape should be filled or outlined, or both
097: */
098: private Style style = Style.BOTH;
099:
100: /**
101: * Create a new ShapePainter
102: */
103: public ShapePainter() {
104: super ();
105: }
106:
107: /**
108: * Create a new ShapePainter with the specified shape.
109: *
110: *
111: * @param shape the shape to fillPaint
112: */
113: public ShapePainter(Shape shape) {
114: super ();
115: this .shape = shape;
116: }
117:
118: /**
119: * Create a new ShapePainter with the specified shape and fillPaint.
120: *
121: *
122: * @param shape the shape to fillPaint
123: * @param paint the fillPaint to be used to fillPaint the shape
124: */
125: public ShapePainter(Shape shape, Paint paint) {
126: super ();
127: this .shape = shape;
128: this .fillPaint = paint;
129: }
130:
131: /**
132: * Create a new ShapePainter with the specified shape and fillPaint. The shape
133: * can be filled or stroked (only the ouline is painted).
134: *
135: *
136: * @param shape the shape to fillPaint
137: * @param paint the fillPaint to be used to fillPaint the shape
138: * @param style specifies the ShapePainter.Style to use for painting this shape.
139: * If null, then Style.BOTH is used
140: */
141: public ShapePainter(Shape shape, Paint paint, Style style) {
142: super ();
143: this .shape = shape;
144: this .fillPaint = paint;
145: this .style = style == null ? Style.BOTH : style;
146: }
147:
148: /**
149: * Sets the shape to fillPaint. This shape is not resized when the component
150: * bounds are. To do that, create a custom shape that is bound to the
151: * component width/height
152: *
153: *
154: * @param s the Shape to fillPaint. May be null
155: */
156: public void setShape(Shape s) {
157: Shape old = getShape();
158: this .shape = s;
159: firePropertyChange("shape", old, getShape());
160: }
161:
162: /**
163: *
164: *
165: * @return the Shape to fillPaint. May be null
166: */
167: public Shape getShape() {
168: return shape;
169: }
170:
171: /**
172: * Sets the stroke to use for painting. If null, then the default Graphics2D
173: * stroke use used
174: *
175: *
176: * @param s the Stroke to fillPaint with
177: */
178: public void setStroke(Stroke s) {
179: Stroke old = getStroke();
180: this .stroke = s;
181: firePropertyChange("stroke", old, getStroke());
182: }
183:
184: /**
185: * @return the Stroke to use for painting
186: */
187: public Stroke getStroke() {
188: return stroke;
189: }
190:
191: /**
192: * The Paint to use for filling the shape. Can be a Color, GradientPaint,
193: * TexturePaint, or any other kind of Paint. If null, the component
194: * background is used.
195: *
196: * @param p the Paint to use for painting the shape. May be null.
197: */
198: public void setFillPaint(Paint p) {
199: Paint old = getFillPaint();
200: this .fillPaint = p;
201: firePropertyChange("fillPaint", old, getFillPaint());
202: }
203:
204: /**
205: * @return the Paint used when painting the shape. May be null
206: */
207: public Paint getFillPaint() {
208: return fillPaint;
209: }
210:
211: /**
212: * The Paint to use for stroking the shape (painting the outline).
213: * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint.
214: * If null, the component foreground is used.
215: *
216: * @param p the Paint to use for stroking the shape. May be null.
217: */
218: public void setStrokePaint(Paint p) {
219: Paint old = getStrokePaint();
220: this .strokePaint = p;
221: firePropertyChange("strokePaint", old, getStrokePaint());
222: }
223:
224: /**
225: * @return the Paint used when stroking the shape. May be null
226: */
227: public Paint getStrokePaint() {
228: return strokePaint;
229: }
230:
231: /**
232: * Specifies the location at which to place the shape prior to painting.
233: * If null, the origin (0,0) is used
234: *
235: *
236: * @param location the Point2D at which to fillPaint the shape. may be null
237: */
238: public void setLocation(Point2D location) {
239: Point2D old = getLocation();
240: this .location = location == null ? new Point2D.Double(0, 0)
241: : location;
242: firePropertyChange("location", old, getLocation());
243: }
244:
245: /**
246: *
247: *
248: * @return the Point2D location at which to fillPaint the shape. Will never be null
249: * (if it was null, new Point2D.Double(0,0) will be returned)
250: */
251: public Point2D getLocation() {
252: return location;
253: }
254:
255: /**
256: * The shape can be filled or simply stroked (outlined), or both. By default,
257: * the shape is both filled and stroked. This property specifies the strategy to
258: * use.
259: *
260: * @param s the Style to use. If null, Style.BOTH is used
261: */
262: public void setStyle(Style s) {
263: Style old = getStyle();
264: this .style = s == null ? Style.BOTH : s;
265: firePropertyChange("style", old, getStyle());
266: }
267:
268: /**
269: * @return the Style used
270: */
271: public Style getStyle() {
272: return style;
273: }
274:
275: /**
276: * Specifies the resize behavior for the location property. If r is
277: * Resize.HORIZONTAL or Resize.BOTH, then the x value of the location
278: * will be treated as if it were a percentage of the width of the component.
279: * Likewise, Resize.VERTICAL or Resize.BOTH will affect the y value. For
280: * example, if I had a location (.3, .8) then the X will be situated at
281: * 30% of the width and the Y will be situated at 80% of the height.
282: *
283: * @param r value indicating whether/how to resize the Location property when
284: * painting. If null, Resize.BOTH will be used
285: */
286: public void setResizeLocation(Resize r) {
287: Resize old = getResizeLocation();
288: this .resizeLocation = r == null ? Resize.NONE : r;
289: firePropertyChange("resizeLocation", old, getResizeLocation());
290: }
291:
292: /**
293: * @return value indication whether/how to resize the location property.
294: * This will never be null
295: */
296: public Resize getResizeLocation() {
297: return resizeLocation;
298: }
299:
300: /**
301: * Specifies the resize behavior of the shape. As with all other properties
302: * that rely on Resize, the value of the width/height of the shape will
303: * represent a percentage of the width/height of the component, as a value
304: * between 0 and 1
305: *
306: * @param r value indication whether/how to resize the shape. If null,
307: * Resize.NONE will be used
308: */
309: public void setResize(Resize r) {
310: Resize old = getResize();
311: this .resize = r == null ? Resize.NONE : r;
312: firePropertyChange("resize", old, getResize());
313: }
314:
315: /**
316: * @return value indication whether/how to resize the shape. Will never be null
317: */
318: public Resize getResize() {
319: return resize;
320: }
321:
322: /**
323: * @inheritDoc
324: */
325: public void paintBackground(Graphics2D g, JComponent component) {
326: //set the stroke if it is not null
327: Stroke s = getStroke();
328: if (s != null) {
329: g.setStroke(s);
330: }
331:
332: //handle the location
333: Point2D location = getLocation();
334: Resize resizeLocation = getResizeLocation();
335: double x = location.getX();
336: double y = location.getY();
337: if (resizeLocation == Resize.HORIZONTAL
338: || resizeLocation == Resize.BOTH) {
339: x = x * component.getWidth();
340: }
341: if (resizeLocation == Resize.VERTICAL
342: || resizeLocation == Resize.BOTH) {
343: y = y * component.getHeight();
344: }
345: g.translate(-location.getX(), -location.getY());
346:
347: //resize the shape if necessary
348: Shape shape = getShape();
349: Rectangle2D bounds = shape.getBounds2D();
350: double width = 1;
351: double height = 1;
352: Resize resize = getResize();
353: if (resize == Resize.HORIZONTAL || resize == Resize.BOTH) {
354: width = component.getWidth() - 1;
355: }
356: if (resize == Resize.VERTICAL || resize == Resize.BOTH) {
357: height = component.getHeight() - 1;
358: }
359:
360: if (shape instanceof RoundRectangle2D) {
361: RoundRectangle2D rect = (RoundRectangle2D) shape;
362: shape = new RoundRectangle2D.Double(rect.getX(), rect
363: .getY(), width, height, rect.getArcWidth(), rect
364: .getArcHeight());
365: } else {
366: shape = AffineTransform.getScaleInstance(width, height)
367: .createTransformedShape(shape);
368: }
369:
370: //draw/fill the shape
371: switch (getStyle()) {
372: case BOTH:
373: g.setPaint(calculateStrokePaint(component));
374: g.draw(shape);
375: g.setPaint(calculateFillPaint(component));
376: g.fill(shape);
377: break;
378: case FILLED:
379: g.setPaint(calculateFillPaint(component));
380: g.fill(shape);
381: break;
382: case OUTLINE:
383: g.setPaint(calculateStrokePaint(component));
384: g.draw(shape);
385: break;
386: }
387: }
388:
389: private Paint calculateStrokePaint(JComponent component) {
390: Paint p = getStrokePaint();
391: if (p == null) {
392: p = component.getForeground();
393: }
394: return p;
395: }
396:
397: private Paint calculateFillPaint(JComponent component) {
398: //set the fillPaint
399: Paint p = getFillPaint();
400: if (p == null) {
401: p = component.getBackground();
402: }
403: return p;
404: }
405: }
|