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 }