001 /*
002 * $Id: JXButton.java 3164 2009-01-01 20:27:17Z 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
022 package org.jdesktop.swingx;
023
024 import java.awt.Graphics;
025 import java.awt.Graphics2D;
026 import java.awt.Insets;
027
028 import javax.swing.*;
029
030 import org.jdesktop.swingx.painter.AbstractPainter;
031 import org.jdesktop.swingx.painter.Painter;
032
033 /**
034 * <p>A {@link org.jdesktop.swingx.painter.Painter} enabled subclass of {@link javax.swing.JButton}.
035 * This class supports setting the foreground and background painters of the button separately. By default,
036 * <code>JXButton</code> creates and installs two <code>Painter</code>s; one for the foreground, and one
037 * for the background. These default <code>Painter</code>s delegate to the installed UI delegate.</p>
038 *
039 * <p>For example, if you wanted to blur <em>just the text</em> on the button, and let everything else be
040 * handled by the UI delegate for your look and feel, then you could:
041 * <pre><code>
042 * JXButton b = new JXButton("Execute");
043 * AbstractPainter fgPainter = (AbstractPainter)b.getForegroundPainter();
044 * StackBlurFilter filter = new StackBlurFilter();
045 * fgPainter.setFilters(filter);
046 * </code></pre>
047 *
048 * <p>If <em>either</em> the foreground painter or the background painter is set,
049 * then super.paintComponent() is not called. By setting both the foreground and background
050 * painters to null, you get <em>exactly</em> the same painting behavior as JButton.
051 * By contrast, the <code>Painters</code> installed by default will delegate to the UI delegate,
052 * thus achieving the same look as a typical JButton, but at the cost of some additional painting
053 * overhead.</p>
054 *
055 * <div class="examples">
056 * <h3>Examples</h3>
057 * {@demo org.jdesktop.swingx.JXButtonDemo ../../../../../demo}
058 * </div>
059 *
060 * @author rbair
061 * @author rah003
062 * @author Jan Stola
063 */
064 public class JXButton extends JButton {
065 //properties used to split foreground and background painting.
066 //overwritten to suppress event notification while painting
067 private String text = "";
068 private boolean borderPainted;
069 private boolean contentAreaFilled;
070
071 private Painter<JXButton> fgPainter = new DefaultForegroundPainter();
072 private Painter<JXButton> bgPainter = new DefaultBackgroundPainter();
073
074 /** Creates a new instance of JXButton */
075 public JXButton() {}
076 public JXButton(String text) {
077 super(text);
078 this.text = text;
079 }
080 public JXButton(Action a) {
081 super();
082 // swingx-849 Has to set action explicitly after UI resources are already initialized by
083 //implicit constructor to ensure properties defined in action are initialized properly.
084 setAction(a);
085 }
086 public JXButton(Icon icon) { super(icon); }
087 public JXButton(String text, Icon icon) {
088 super(text, icon);
089 this.text = text;
090 }
091
092 @Override
093 protected void init(String text, Icon icon) {
094 borderPainted = true;
095 contentAreaFilled = true;
096 super.init(text, icon);
097 }
098
099 @Override
100 public void setText(String text) {
101 this.text = text;
102 super.setText(text);
103 }
104 @Override
105 public void repaint() {
106 if (painting) {
107 // skip repaint requests while painting
108 return;
109 }
110 super.repaint();
111 }
112
113 @Override
114 public String getText() {
115 return this.text;
116 }
117
118 @Override
119 public void setBorderPainted(boolean b) {
120 this.borderPainted = b;
121 super.setBorderPainted(b);
122 }
123
124 @Override
125 public boolean isBorderPainted() {
126 return this.borderPainted;
127 }
128
129 @Override
130 public void setContentAreaFilled(boolean b) {
131 this.contentAreaFilled = b;
132 super.setContentAreaFilled(b);
133 }
134
135 @Override
136 public boolean isContentAreaFilled() {
137 return this.contentAreaFilled;
138 }
139
140 public Painter<JXButton> getBackgroundPainter() {
141 return bgPainter;
142 }
143
144 public void setBackgroundPainter(Painter<JXButton> p) {
145 Painter old = getBackgroundPainter();
146 this.bgPainter = p;
147 firePropertyChange("backgroundPainter", old, getBackgroundPainter());
148 repaint();
149 }
150 public Painter<JXButton> getForegroundPainter() {
151 return fgPainter;
152 }
153
154 public void setForegroundPainter(Painter<JXButton> p) {
155 Painter old = getForegroundPainter();
156 this.fgPainter = p;
157 firePropertyChange("foregroundPainter", old, getForegroundPainter());
158 repaint();
159 }
160
161 private boolean paintBorderInsets = true;
162 private boolean painting;
163 private boolean opaque = true;
164
165 /**
166 * Returns true if the background painter should paint where the border is
167 * or false if it should only paint inside the border. This property is
168 * true by default. This property affects the width, height,
169 * and intial transform passed to the background painter.
170 */
171 public boolean isPaintBorderInsets() {
172 return paintBorderInsets;
173 }
174
175 /**
176 * Sets the paintBorderInsets property.
177 * Set to true if the background painter should paint where the border is
178 * or false if it should only paint inside the border. This property is true by default.
179 * This property affects the width, height,
180 * and intial transform passed to the background painter.
181 *
182 * This is a bound property.
183 */
184 public void setPaintBorderInsets(boolean paintBorderInsets) {
185 boolean old = this.isPaintBorderInsets();
186 this.paintBorderInsets = paintBorderInsets;
187 firePropertyChange("paintBorderInsets", old, isPaintBorderInsets());
188 }
189
190 @Override
191 public boolean isOpaque() {
192 return painting ? opaque : super.isOpaque();
193 }
194
195 protected void paintComponent(Graphics g) {
196 Painter<JXButton> bgPainter = getBackgroundPainter();
197 Painter<JXButton> fgPainter = getForegroundPainter();
198 if (painting || (bgPainter == null && fgPainter == null)) {
199 super.paintComponent(g);
200 } else {
201 invokePainter(g, bgPainter);
202 invokePainter(g, fgPainter);
203 }
204 }
205
206 private void invokePainter(Graphics g, Painter<JXButton> ptr) {
207 if(ptr == null) return;
208
209 Graphics2D g2d = (Graphics2D) g.create();
210
211 try {
212 if(isPaintBorderInsets()) {
213 ptr.paint(g2d, this, getWidth(), getHeight());
214 } else {
215 Insets ins = this.getInsets();
216 g2d.translate(ins.left, ins.top);
217 ptr.paint(g2d, this,
218 this.getWidth() - ins.left - ins.right,
219 this.getHeight() - ins.top - ins.bottom);
220 }
221 } finally {
222 g2d.dispose();
223 }
224 }
225 // paint anything but text and icon
226 private static final class DefaultBackgroundPainter extends AbstractPainter<JXButton> {
227 protected void doPaint(Graphics2D g, JXButton b, int width, int height) {
228 boolean op = b.opaque;
229 // have to read this before setting painting == true !!!
230 b.opaque = b.isOpaque();
231 b.setPainting(true);
232 String tmp = b.text;
233 // #swingx-874
234 Icon tmpIcon = b.getIcon();
235 b.setIcon(null);
236 b.text = "";
237 try {
238 b.paint(g);
239 } finally {
240 // restore original values no matter what
241 b.opaque = op;
242 b.text = tmp;
243 b.setIcon(tmpIcon);
244 b.setPainting(false);
245 }
246 }
247
248 //if any of the state of the JButton that affects the background has changed,
249 //then I must clear the cache. This is really hard to get right, there are
250 //bound to be bugs. An alternative is to NEVER cache.
251 protected boolean shouldUseCache() {
252 return false;
253 }
254 }
255 // paint only a text and icon (if any)
256 private static final class DefaultForegroundPainter extends AbstractPainter<JXButton> {
257 protected void doPaint(Graphics2D g, JXButton b, int width, int height) {
258 b.setPainting(true);
259 boolean t1 = b.isBorderPainted();
260 boolean t2 = b.isContentAreaFilled();
261 boolean op = b.opaque;
262 b.borderPainted = false;
263 b.contentAreaFilled = false;
264 b.opaque = false;
265 try {
266 b.paint(g);
267 } finally {
268 // restore original values no matter what
269 b.opaque = op;
270 b.borderPainted = t1;
271 b.contentAreaFilled = t2;
272 b.setPainting(false);
273 }
274 }
275
276 //if any of the state of the JButton that affects the foreground has changed,
277 //then I must clear the cache. This is really hard to get right, there are
278 //bound to be bugs. An alternative is to NEVER cache.
279 protected boolean shouldUseCache() {
280 return false;
281 }
282 }
283
284 protected void setPainting(boolean b) {
285 painting = b;
286 }
287
288 }