/*
* $Id: JXButton.java,v 1.16 2009/01/01 20:27:17 rah003 Exp $
*
* Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import javax.swing.*;
import org.jdesktop.swingx.painter.AbstractPainter;
import org.jdesktop.swingx.painter.Painter;
/**
* <p>A {@link org.jdesktop.swingx.painter.Painter} enabled subclass of {@link javax.swing.JButton}.
* This class supports setting the foreground and background painters of the button separately. By default,
* <code>JXButton</code> creates and installs two <code>Painter</code>s; one for the foreground, and one
* for the background. These default <code>Painter</code>s delegate to the installed UI delegate.</p>
*
* <p>For example, if you wanted to blur <em>just the text</em> on the button, and let everything else be
* handled by the UI delegate for your look and feel, then you could:
* <pre><code>
* JXButton b = new JXButton("Execute");
* AbstractPainter fgPainter = (AbstractPainter)b.getForegroundPainter();
* StackBlurFilter filter = new StackBlurFilter();
* fgPainter.setFilters(filter);
* </code></pre>
*
* <p>If <em>either</em> the foreground painter or the background painter is set,
* then super.paintComponent() is not called. By setting both the foreground and background
* painters to null, you get <em>exactly</em> the same painting behavior as JButton.
* By contrast, the <code>Painters</code> installed by default will delegate to the UI delegate,
* thus achieving the same look as a typical JButton, but at the cost of some additional painting
* overhead.</p>
*
* <div class="examples">
* <h3>Examples</h3>
* {@demo org.jdesktop.swingx.JXButtonDemo ../../../../../demo}
* </div>
*
* @author rbair
* @author rah003
* @author Jan Stola
*/
public class JXButton extends JButton {
//properties used to split foreground and background painting.
//overwritten to suppress event notification while painting
private String text = "";
private boolean borderPainted;
private boolean contentAreaFilled;
private Painter<JXButton> fgPainter = new DefaultForegroundPainter();
private Painter<JXButton> bgPainter = new DefaultBackgroundPainter();
/** Creates a new instance of JXButton */
public JXButton() {}
public JXButton(String text) {
super(text);
this.text = text;
}
public JXButton(Action a) {
super();
// swingx-849 Has to set action explicitly after UI resources are already initialized by
//implicit constructor to ensure properties defined in action are initialized properly.
setAction(a);
}
public JXButton(Icon icon) { super(icon); }
public JXButton(String text, Icon icon) {
super(text, icon);
this.text = text;
}
@Override
protected void init(String text, Icon icon) {
borderPainted = true;
contentAreaFilled = true;
super.init(text, icon);
}
@Override
public void setText(String text) {
this.text = text;
super.setText(text);
}
@Override
public void repaint() {
if (painting) {
// skip repaint requests while painting
return;
}
super.repaint();
}
@Override
public String getText() {
return this.text;
}
@Override
public void setBorderPainted(boolean b) {
this.borderPainted = b;
super.setBorderPainted(b);
}
@Override
public boolean isBorderPainted() {
return this.borderPainted;
}
@Override
public void setContentAreaFilled(boolean b) {
this.contentAreaFilled = b;
super.setContentAreaFilled(b);
}
@Override
public boolean isContentAreaFilled() {
return this.contentAreaFilled;
}
public Painter<JXButton> getBackgroundPainter() {
return bgPainter;
}
public void setBackgroundPainter(Painter<JXButton> p) {
Painter old = getBackgroundPainter();
this.bgPainter = p;
firePropertyChange("backgroundPainter", old, getBackgroundPainter());
repaint();
}
public Painter<JXButton> getForegroundPainter() {
return fgPainter;
}
public void setForegroundPainter(Painter<JXButton> p) {
Painter old = getForegroundPainter();
this.fgPainter = p;
firePropertyChange("foregroundPainter", old, getForegroundPainter());
repaint();
}
private boolean paintBorderInsets = true;
private boolean painting;
private boolean opaque = true;
/**
* Returns true if the background painter should paint where the border is
* or false if it should only paint inside the border. This property is
* true by default. This property affects the width, height,
* and intial transform passed to the background painter.
*/
public boolean isPaintBorderInsets() {
return paintBorderInsets;
}
/**
* Sets the paintBorderInsets property.
* Set to true if the background painter should paint where the border is
* or false if it should only paint inside the border. This property is true by default.
* This property affects the width, height,
* and intial transform passed to the background painter.
*
* This is a bound property.
*/
public void setPaintBorderInsets(boolean paintBorderInsets) {
boolean old = this.isPaintBorderInsets();
this.paintBorderInsets = paintBorderInsets;
firePropertyChange("paintBorderInsets", old, isPaintBorderInsets());
}
@Override
public boolean isOpaque() {
return painting ? opaque : super.isOpaque();
}
@Override
protected void paintComponent(Graphics g) {
Painter<JXButton> bgPainter = getBackgroundPainter();
Painter<JXButton> fgPainter = getForegroundPainter();
if (painting || (bgPainter == null && fgPainter == null)) {
super.paintComponent(g);
} else {
invokePainter(g, bgPainter);
invokePainter(g, fgPainter);
}
}
private void invokePainter(Graphics g, Painter<JXButton> ptr) {
if(ptr == null) return;
Graphics2D g2d = (Graphics2D) g.create();
try {
if(isPaintBorderInsets()) {
ptr.paint(g2d, this, getWidth(), getHeight());
} else {
Insets ins = this.getInsets();
g2d.translate(ins.left, ins.top);
ptr.paint(g2d, this,
this.getWidth() - ins.left - ins.right,
this.getHeight() - ins.top - ins.bottom);
}
} finally {
g2d.dispose();
}
}
// paint anything but text and icon
private static final class DefaultBackgroundPainter extends AbstractPainter<JXButton> {
@Override
protected void doPaint(Graphics2D g, JXButton b, int width, int height) {
boolean op = b.opaque;
// have to read this before setting painting == true !!!
b.opaque = b.isOpaque();
b.setPainting(true);
String tmp = b.text;
// #swingx-874
Icon tmpIcon = b.getIcon();
b.setIcon(null);
b.text = "";
try {
b.paint(g);
} finally {
// restore original values no matter what
b.opaque = op;
b.text = tmp;
b.setIcon(tmpIcon);
b.setPainting(false);
}
}
//if any of the state of the JButton that affects the background has changed,
//then I must clear the cache. This is really hard to get right, there are
//bound to be bugs. An alternative is to NEVER cache.
@Override
protected boolean shouldUseCache() {
return false;
}
}
// paint only a text and icon (if any)
private static final class DefaultForegroundPainter extends AbstractPainter<JXButton> {
@Override
protected void doPaint(Graphics2D g, JXButton b, int width, int height) {
b.setPainting(true);
boolean t1 = b.isBorderPainted();
boolean t2 = b.isContentAreaFilled();
boolean op = b.opaque;
b.borderPainted = false;
b.contentAreaFilled = false;
b.opaque = false;
try {
b.paint(g);
} finally {
// restore original values no matter what
b.opaque = op;
b.borderPainted = t1;
b.contentAreaFilled = t2;
b.setPainting(false);
}
}
//if any of the state of the JButton that affects the foreground has changed,
//then I must clear the cache. This is really hard to get right, there are
//bound to be bugs. An alternative is to NEVER cache.
@Override
protected boolean shouldUseCache() {
return false;
}
}
protected void setPainting(boolean b) {
painting = b;
}
}