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 }