001 /*
002 * $Id: IconBorder.java 2528 2007-12-14 13:49:37Z stolis $
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.border;
023
024 import java.awt.Component;
025 import java.awt.ComponentOrientation;
026 import java.awt.Graphics;
027 import java.awt.Insets;
028 import java.awt.Rectangle;
029 import java.io.Serializable;
030
031 import javax.swing.Icon;
032 import javax.swing.SwingConstants;
033 import javax.swing.border.Border;
034
035 import org.jdesktop.swingx.icon.EmptyIcon;
036
037 /**
038 * {@code IconBorder} creates a border that places an {@code Icon} in the border
039 * on the horizontal axis. The border does not add any additional insets other
040 * than the inset required to produce the space for the icon. If additional
041 * insets are required, users should create a
042 * {@link javax.swing.border.CompoundBorder compund border}.
043 * <p>
044 * This border is useful when attempting to add {@code Icon}s to pre-existing
045 * components without requiring specialty painting.
046 *
047 * @author Amy Fowler
048 * @author Karl Schaefer
049 *
050 * @version 1.1
051 */
052 public class IconBorder implements Border, Serializable {
053
054 /**
055 * An empty icon.
056 */
057 public static final Icon EMPTY_ICON = new EmptyIcon();
058 private int padding;
059 private Icon icon;
060 private int iconPosition;
061 private Rectangle iconBounds = new Rectangle();
062
063 /**
064 * Creates an {@code IconBorder} with an empty icon in a trailing position
065 * with a padding of 4.
066 *
067 * @see #EMPTY_ICON
068 */
069 public IconBorder() {
070 this(null);
071 }
072
073 /**
074 * Creates an {@code IconBorder} with the specified icon in a trailing
075 * position with a padding of 4.
076 *
077 * @param validIcon
078 * the icon to set. This may be {@code null} to represent an
079 * empty icon.
080 * @see #EMPTY_ICON
081 */
082 public IconBorder(Icon validIcon) {
083 this(validIcon, SwingConstants.TRAILING);
084 }
085
086 /**
087 * Creates an {@code IconBorder} with the specified constraints and a
088 * padding of 4.
089 *
090 * @param validIcon
091 * the icon to set. This may be {@code null} to represent an
092 * empty icon.
093 * @param iconPosition
094 * the position to place the icon relative to the component
095 * contents. This must be one of the following
096 * {@code SwingConstants}:
097 * <ul>
098 * <li>{@code LEADING}</li>
099 * <li>{@code TRAILING}</li>
100 * <li>{@code EAST}</li>
101 * <li>{@code WEST}</li>
102 * </ul>
103 * @throws IllegalArgumentException
104 * if {@code iconPosition} is not a valid position.
105 * @see #EMPTY_ICON
106 */
107 public IconBorder(Icon validIcon, int iconPosition) {
108 this(validIcon, iconPosition, 4);
109 }
110
111 /**
112 * Creates an {@code IconBorder} with the specified constraints. If
113 * {@code validIcon} is {@code null}, {@code EMPTY_ICON} is used instead.
114 * If {@code padding} is negative, then the border does not use padding.
115 *
116 * @param validIcon
117 * the icon to set. This may be {@code null} to represent an
118 * empty icon.
119 * @param iconPosition
120 * the position to place the icon relative to the component
121 * contents. This must be one of the following
122 * {@code SwingConstants}:
123 * <ul>
124 * <li>{@code LEADING}</li>
125 * <li>{@code TRAILING}</li>
126 * <li>{@code EAST}</li>
127 * <li>{@code WEST}</li>
128 * </ul>
129 * @param padding
130 * the padding to surround the icon with. All non-positive values
131 * set the padding to 0.
132 * @throws IllegalArgumentException
133 * if {@code iconPosition} is not a valid position.
134 * @see #EMPTY_ICON
135 */
136 public IconBorder(Icon validIcon, int iconPosition, int padding) {
137 setIcon(validIcon);
138 setPadding(padding);
139 setIconPosition(iconPosition);
140 }
141
142 private boolean isValidPosition(int position) {
143 boolean result = false;
144
145 switch (position) {
146 case SwingConstants.LEADING:
147 case SwingConstants.TRAILING:
148 case SwingConstants.EAST:
149 case SwingConstants.WEST:
150 result = true;
151 break;
152 default:
153 result = false;
154 }
155
156 return result;
157 }
158
159 /**
160 * {@inheritDoc}
161 */
162 public Insets getBorderInsets(Component c) {
163 int horizontalInset = icon.getIconWidth() + (2 * padding);
164 int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(), this.iconPosition);
165 if (iconPosition == SwingConstants.EAST) {
166 return new Insets(0, 0, 0, horizontalInset);
167 }
168 return new Insets(0, horizontalInset, 0, 0);
169 }
170
171 /**
172 * Sets the icon for this border.
173 *
174 * @param validIcon
175 * the icon to set. This may be {@code null} to represent an
176 * empty icon.
177 * @see #EMPTY_ICON
178 */
179 public void setIcon(Icon validIcon) {
180 this.icon = validIcon == null ? EMPTY_ICON : validIcon;
181 }
182
183 /**
184 * This border is not opaque.
185 *
186 * @return always returns {@code false}
187 */
188 public boolean isBorderOpaque() {
189 return false;
190 }
191
192 /**
193 * {@inheritDoc}
194 */
195 public void paintBorder(Component c, Graphics g, int x, int y, int width,
196 int height) {
197 int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(), this.iconPosition);
198 if (iconPosition == SwingConstants.NORTH_EAST) {
199 iconBounds.y = y + padding;
200 iconBounds.x = x + width - padding - icon.getIconWidth();
201 } else if (iconPosition == SwingConstants.EAST) { // EAST
202 iconBounds.y = y
203 + ((height - icon.getIconHeight()) / 2);
204 iconBounds.x = x + width - padding - icon.getIconWidth();
205 } else if (iconPosition == SwingConstants.WEST) {
206 iconBounds.y = y
207 + ((height - icon.getIconHeight()) / 2);
208 iconBounds.x = x + padding;
209 }
210 iconBounds.width = icon.getIconWidth();
211 iconBounds.height = icon.getIconHeight();
212 icon.paintIcon(c, g, iconBounds.x, iconBounds.y);
213 }
214
215 /**
216 * Returns EAST or WEST depending on the ComponentOrientation and
217 * the given postion LEADING/TRAILING this method has no effect for other
218 * position values
219 */
220 private int bidiDecodeLeadingTrailing(ComponentOrientation c, int position) {
221 if(position == SwingConstants.TRAILING) {
222 if(!c.isLeftToRight()) {
223 return SwingConstants.WEST;
224 }
225 return SwingConstants.EAST;
226 }
227 if(position == SwingConstants.LEADING) {
228 if(c.isLeftToRight()) {
229 return SwingConstants.WEST;
230 }
231 return SwingConstants.EAST;
232 }
233 return position;
234 }
235
236 /**
237 * Gets the padding surrounding the icon.
238 *
239 * @return the padding for the icon. This value is guaranteed to be
240 * nonnegative.
241 */
242 public int getPadding() {
243 return padding;
244 }
245
246 /**
247 * Sets the padding around the icon.
248 *
249 * @param padding
250 * the padding to set. If {@code padding < 0}, then
251 * {@code padding} will be set to {@code 0}.
252 */
253 public void setPadding(int padding) {
254 this.padding = padding < 0 ? 0 : padding;
255 }
256
257 /**
258 * Returns the position to place the icon (relative to the component contents).
259 *
260 * @return one of the following {@code SwingConstants}:
261 * <ul>
262 * <li>{@code LEADING}</li>
263 * <li>{@code TRAILING}</li>
264 * <li>{@code EAST}</li>
265 * <li>{@code WEST}</li>
266 * </ul>
267 */
268 public int getIconPosition() {
269 return iconPosition;
270 }
271
272 /**
273 * Sets the position to place the icon (relative to the component contents).
274 *
275 * @param iconPosition must be one of the following {@code SwingConstants}:
276 * <ul>
277 * <li>{@code LEADING}</li>
278 * <li>{@code TRAILING}</li>
279 * <li>{@code EAST}</li>
280 * <li>{@code WEST}</li>
281 * </ul>
282 * @throws IllegalArgumentException
283 * if {@code iconPosition} is not a valid position.
284 */
285 public void setIconPosition(int iconPosition) {
286 if (!isValidPosition(iconPosition)) {
287 throw new IllegalArgumentException("Invalid icon position");
288 }
289 this.iconPosition = iconPosition;
290 }
291
292 }