001 /*
002 * $Id: AbstractLayoutPainter.java 3100 2008-10-14 22:33:10Z rah003 $
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 package org.jdesktop.swingx.painter;
022
023 import java.awt.Insets;
024 import java.awt.Rectangle;
025
026 /**
027 * An abstract base class for any painter which can be positioned. This means
028 * the painter has some intrinsic size to what it is drawing and
029 * can be stretched or aligned both horizontally and vertically.
030 *
031 *
032 * The AbstractLayoutPainter class provides the following configuraable properties:
033 *
034 * <ul>
035 * <li>horizonalAlignment - the horizonal alignment (left, center, and right)</li>
036 * <li>verticalAlignment - the verticalAlignment alignment (top, center, and bottom)</li>
037 * <li>fillHorizontal - indicates if the painter should stretch to fill the available space horizontally</li>
038 * <li>fillVertical - indicates if the painter should stretch to fill the available space vertically</li>
039 * <li>insets - whitespace on the top, bottom, left, and right.
040 * </ul>
041 *
042 * By combining these five properties any AbstractLayoutPainter subclass can position it's content
043 * within the paintable area. For example, an ImagePainter has an intrinsic size based on the image
044 * it is painting. If you wanted to paint the image in the lower right hand corner of the paintable
045 * area, but inset by 5 pixels, you could do the following:
046 *
047 * <pre><code>
048 * ImagePainter p = new ImagePainter(null);
049 * p.setVerticalAlignment(AbstractLayoutPainter.VerticalAlignment.BOTTOM);
050 * p.setHorizontalAlignment(AbstractLayoutPainter.HorizontalAlignment.RIGHT);
051 * p.setInsets(new Insets(0,0,5,5));
052 * </code></pre>
053 *
054 *
055 * For something which is resizable, like a RectanglePainter, you can use the fill properties
056 * to make it resize along with the paintable area. For example, to make a rectangle with 20 px
057 * rounded corners, and which resizes with the paintable area but is inset
058 * by 10 pixels on all sides, you could do
059 * the following:
060 *
061 * <pre><code>
062 * RectanglePainter p = new RectanglePainter();
063 * p.setRoundHeight(20);
064 * p.setRoundWidth(20);
065 * p.setInsets(new Insets(10,10,10,10));
066 * p.setFillHorizontal(true);
067 * p.setFillVertical(true);
068 * </code></pre>
069 *
070 *
071 * @author joshua@marinacci.org
072 */
073 public abstract class AbstractLayoutPainter<T> extends AbstractPainter<T> {
074
075 /**
076 * Specifies how to draw the image, i.e. what kind of Style to use
077 * when drawing
078 */
079 private VerticalAlignment verticalAlignment = VerticalAlignment.CENTER;
080 private HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER;
081 private Insets insets = new Insets(0,0,0,0);
082 private boolean fillVertical = false;
083 private boolean fillHorizontal = false;
084
085 /**
086 * Creates a new instance of AbstractLayoutPainter
087 */
088 public AbstractLayoutPainter() {
089 }
090
091 /**
092 * An enum which controls horizontalAlignment alignment
093 */
094 public static enum HorizontalAlignment { LEFT, CENTER, RIGHT }
095
096
097 /**
098 * An enum which controls verticalAlignment alignment
099 */
100 public static enum VerticalAlignment { TOP, CENTER, BOTTOM }
101
102
103 /**
104 * Gets the current horizontalAlignment alignment.
105 *
106 * @return the current horizontalAlignment alignment
107 */
108 public HorizontalAlignment getHorizontalAlignment() {
109 return horizontalAlignment;
110 }
111
112
113 /**
114 * Gets the current whitespace insets.
115 * @return the current insets
116 */
117 public Insets getInsets() {
118 return insets;
119 }
120
121
122 /**
123 * gets the current verticalAlignment alignment
124 *
125 * @return current verticalAlignment alignment
126 */
127 public VerticalAlignment getVerticalAlignment() {
128 return verticalAlignment;
129 }
130
131
132 /**
133 * indicates if the painter content is stretched horizontally
134 *
135 * @return the current horizontalAlignment stretch value
136 */
137 public boolean isFillHorizontal() {
138 return fillHorizontal;
139 }
140
141
142 /**
143 * indicates if the painter content is stretched vertically
144 *
145 * @return the current verticalAlignment stretch value
146 */
147 public boolean isFillVertical() {
148 return fillVertical;
149 }
150
151
152 /**
153 * Sets a new horizontalAlignment alignment. Used to position the content at the left, right, or center.
154 *
155 * @param horizontal new horizontalAlignment alignment
156 */
157 public void setHorizontalAlignment(HorizontalAlignment horizontal) {
158 HorizontalAlignment old = this.getHorizontalAlignment();
159 this.horizontalAlignment = horizontal;
160 setDirty(true);
161 firePropertyChange("horizontal",old,this.horizontalAlignment);
162 }
163
164
165 /**
166 * Sets if the content should be stretched horizontally to fill all available horizontalAlignment
167 * space (minus the left and right insets).
168 *
169 *
170 * @param fillHorizontal new horizonal stretch value
171 */
172 public void setFillHorizontal(boolean fillHorizontal) {
173 boolean old = this.isFillHorizontal();
174 this.fillHorizontal = fillHorizontal;
175 setDirty(true);
176 firePropertyChange("horizontalStretch",old,this.fillHorizontal);
177 }
178
179
180 /**
181 * Sets the current whitespace insets.
182 * @param insets new insets
183 */
184 public void setInsets(Insets insets) {
185 Insets old = this.getInsets();
186 this.insets = insets;
187 setDirty(true);
188 firePropertyChange("insets",old,this.insets);
189 }
190
191
192
193 /**
194 * Sets a new verticalAlignment alignment. Used to position the content at the top, bottom, or center.
195 *
196 * @param vertical new verticalAlignment alignment
197 */
198 public void setVerticalAlignment(VerticalAlignment vertical) {
199 VerticalAlignment old = this.getVerticalAlignment();
200 this.verticalAlignment = vertical;
201 setDirty(true);
202 firePropertyChange("vertical",old,this.verticalAlignment);
203 }
204
205
206 /**
207 * Sets if the content should be stretched vertically to fill all available verticalAlignment
208 * space (minus the top and bottom insets).
209 *
210 *
211 * @param verticalStretch new verticalAlignment stretch value
212 */
213 public void setFillVertical(boolean verticalStretch) {
214 boolean old = this.isFillVertical();
215 this.fillVertical = verticalStretch;
216 setDirty(true);
217 firePropertyChange("verticalStretch",old,this.fillVertical);
218 }
219
220 /**
221 * A protected method used by subclasses to calculate the final position of the
222 * content. This will position the content using the fillHorizontal, fillVertical
223 * horizontalAlignment, and verticalAlignment properties. This method
224 * is typically called by subclasses in their doPaint() methods.
225 *
226 * @param contentWidth The width of the content to be painted
227 * @param contentHeight The height of the content to be painted
228 * @param width the width of the area that the content will be positioned in
229 * @param height the height of the area that the content will be positioned in
230 * @return the rectangle for the content to be painted in
231 */
232 protected final Rectangle calculateLayout(final int contentWidth, final int contentHeight,
233 final int width, final int height) {
234
235 Rectangle rect = new Rectangle();
236 rect.width = contentWidth;
237 rect.height = contentHeight;
238
239 if(isFillHorizontal()) {
240 rect.width = width - insets.left - insets.right;
241 }
242
243 if(isFillVertical()) {
244 rect.height = height - insets.top - insets.bottom;
245 }
246 rect.x = calculateX(rect.width, width);
247 rect.y = calculateY(rect.height, height);
248 return rect;
249 }
250
251 private int calculateY(final int imgHeight, final int height) {
252 int y = 0;
253 if(getVerticalAlignment() == VerticalAlignment.TOP) {
254 y = 0;
255 y+= insets.top;
256 }
257 if(getVerticalAlignment() == VerticalAlignment.CENTER) {
258 y = (height-imgHeight)/2;
259 y += insets.top;
260 }
261 if(getVerticalAlignment() == VerticalAlignment.BOTTOM) {
262 y = height-imgHeight;
263 y-= insets.bottom;
264 }
265 return y;
266 }
267
268 private int calculateX(final int imgWidth, final int width) {
269 int x = 0;
270 if(getHorizontalAlignment() == HorizontalAlignment.LEFT) {
271 x = 0;
272 x+= insets.left;
273 }
274 if(getHorizontalAlignment() == HorizontalAlignment.CENTER) {
275 x = (width-imgWidth)/2;
276 x += insets.left;
277 }
278 if(getHorizontalAlignment() == HorizontalAlignment.RIGHT) {
279 x = width-imgWidth;
280 x-= insets.right;
281 }
282 return x;
283 }
284 }