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    }