001/*
002 * $Id: AbstractLayoutPainter.java 4079 2011-11-15 16:05:13Z 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 */
021package org.jdesktop.swingx.painter;
022
023import java.awt.Insets;
024import 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@SuppressWarnings("nls")
074public abstract class AbstractLayoutPainter<T> extends AbstractPainter<T> {
075   
076    /**
077     * Specifies how to draw the image, i.e. what kind of Style to use
078     * when drawing
079     */
080    private VerticalAlignment verticalAlignment = VerticalAlignment.CENTER;
081    private HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER;
082    private Insets insets = new Insets(0,0,0,0);
083    private boolean fillVertical = false;
084    private boolean fillHorizontal = false;
085
086    /**
087     * Creates a new instance of AbstractLayoutPainter
088     */
089    public AbstractLayoutPainter() {
090    }
091
092    /**
093     * An enum which controls horizontalAlignment alignment
094     */
095    public static enum HorizontalAlignment { LEFT, CENTER, RIGHT }
096    
097    /**
098     * An enum which controls verticalAlignment alignment
099     */
100    public static enum VerticalAlignment { TOP, CENTER, BOTTOM }
101    
102    /**
103     * Gets the current horizontalAlignment alignment.
104     * 
105     * @return the current horizontalAlignment alignment
106     */
107    public HorizontalAlignment getHorizontalAlignment() {
108        return horizontalAlignment;
109    }
110
111    /**
112     * Gets the current whitespace insets.
113     * @return the current insets
114     */
115    public Insets getInsets() {
116        return insets;
117    }
118
119    /**
120     * gets the current verticalAlignment alignment
121     * 
122     * @return current verticalAlignment alignment
123     */
124    public VerticalAlignment getVerticalAlignment() {
125        return verticalAlignment;
126    }
127
128    /**
129     * indicates if the painter content is stretched horizontally
130     * 
131     * @return the current horizontalAlignment stretch value
132     */
133    public boolean isFillHorizontal() {
134        return fillHorizontal;
135    }
136
137    /**
138     * indicates if the painter content is stretched vertically
139     * 
140     * @return the current verticalAlignment stretch value
141     */
142    public boolean isFillVertical() {
143        return fillVertical;
144    }
145
146    /**
147     * Sets a new horizontalAlignment alignment. Used to position the content at the left, right, or center.
148     * 
149     * @param horizontal new horizontalAlignment alignment
150     */
151    public void setHorizontalAlignment(HorizontalAlignment horizontal) {
152        HorizontalAlignment old = this.getHorizontalAlignment();
153        this.horizontalAlignment = horizontal;
154        setDirty(true);
155        firePropertyChange("horizontalAlignment", old, getHorizontalAlignment());
156    }
157
158    /**
159     * Sets if the content should be stretched horizontally to fill all available horizontalAlignment
160     * space (minus the left and right insets).
161     * 
162     * @param fillHorizontal new horizontal stretch value
163     */
164    public void setFillHorizontal(boolean fillHorizontal) {
165        boolean old = this.isFillHorizontal();
166        this.fillHorizontal = fillHorizontal;
167        setDirty(true);
168        firePropertyChange("fillHorizontal", old, isFillHorizontal());
169    }
170    
171    /**
172     * Sets the current whitespace insets.
173     * @param insets new insets
174     */
175    public void setInsets(Insets insets) {
176        Insets old = this.getInsets();
177        this.insets = insets;
178        setDirty(true);
179        firePropertyChange("insets", old, getInsets());
180    }
181    
182    /**
183     * Sets a new verticalAlignment alignment. Used to position the content at the top, bottom, or center.
184     * 
185     * @param vertical new verticalAlignment alignment
186     */
187    public void setVerticalAlignment(VerticalAlignment vertical) {
188        VerticalAlignment old = this.getVerticalAlignment();
189        this.verticalAlignment = vertical;
190        setDirty(true);
191        firePropertyChange("verticalAlignment", old, getVerticalAlignment());
192    }
193
194    /**
195     * Sets if the content should be stretched vertically to fill all available verticalAlignment
196     * space (minus the top and bottom insets).
197     * 
198     * @param verticalStretch new verticalAlignment stretch value
199     */
200    public void setFillVertical(boolean verticalStretch) {
201        boolean old = this.isFillVertical();
202        this.fillVertical = verticalStretch;
203        setDirty(true);
204        firePropertyChange("fillVertical", old, isFillVertical());
205    }
206    
207    /**
208     * A protected method used by subclasses to calculate the final position of the
209     * content. This will position the content using the fillHorizontal, fillVertical
210     * horizontalAlignment, and verticalAlignment properties. This method
211     * is typically called by subclasses in their doPaint() methods.
212     * 
213     * @param contentWidth The width of the content to be painted
214     * @param contentHeight The height of the content to be painted
215     * @param width the width of the area that the content will be positioned in
216     * @param height the height of the area that the content will be positioned in
217     * @return the rectangle for the content to be painted in
218     */
219    protected final Rectangle calculateLayout(final int contentWidth, final int contentHeight, 
220            final int width, final int height) {
221        
222        Rectangle rect = new Rectangle();
223        rect.width = contentWidth;
224        rect.height = contentHeight;
225        
226        if(isFillHorizontal()) {
227            rect.width = width - insets.left - insets.right;
228        }
229        
230        if(isFillVertical()) {
231            rect.height = height - insets.top - insets.bottom;
232        }
233        rect.x = calculateX(rect.width, width);
234        rect.y = calculateY(rect.height, height);
235        return rect;
236    }
237
238    private int calculateY(final int imgHeight, final int height) {
239        int y = 0;
240        if(getVerticalAlignment() == VerticalAlignment.TOP) {
241            y = 0;
242            y+= insets.top;
243        }
244        if(getVerticalAlignment() == VerticalAlignment.CENTER) {
245            y = (height-imgHeight)/2;
246            y += insets.top;
247        }
248        if(getVerticalAlignment() == VerticalAlignment.BOTTOM) {
249            y = height-imgHeight;
250            y-= insets.bottom;
251        }
252        return y;
253    }
254
255    private int calculateX(final int imgWidth, final int width) {
256        int x = 0;
257        if(getHorizontalAlignment() == HorizontalAlignment.LEFT) {
258            x = 0;
259            x+= insets.left;
260        }
261        if(getHorizontalAlignment() == HorizontalAlignment.CENTER) {
262            x = (width-imgWidth)/2;
263            x += insets.left;
264        }
265        if(getHorizontalAlignment() == HorizontalAlignment.RIGHT) {
266            x = width-imgWidth;
267            x-= insets.right;
268        }
269        return x;
270    }
271}