Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4783068
Votes 47
Synopsis Components with HTML text should gray out the text when disabled
Category java:classes_swing
Reported Against 1.3
Release Fixed 7(b55)
State 10-Fix Delivered, request for enhancement
Priority: 5-Very Low
Related Bugs
Submit Date 22-NOV-2002
Description
Up through 1.4, there's no easy way to gray out the text of a component
(say, a button) that uses HTML formatting and is disabled.  HTML formatting
would be easier to use if there were such a mechanism.  The engineer says:

  We'd probably add a property to allow users to turn it on or off
  (or to customize the disabled appearance themselves).
Work Around
N/A
Evaluation
Many views do not check whether their containers are disabled. And GlyphView does a check only if its host is a JTextComponent.

We could add checks and paint with "textInactiveText" color if the host is disabled.
Posted Date : 2009-03-13 14:16:27.0
Comments
  
  Include a link with my name & email   

Submitted On 23-OCT-2003
stephanemorin
may be i will say something stupid, but can't we do a extends 
method which able/disable AND set black/grey in same time?


Submitted On 03-JAN-2004
daleanson
This class works fairly well, use it in place of JButton
where needed:

import javax.swing.*;

public class HtmlButton extends JButton {
   private String html = "<html>";
   private String disabledColor = "<font color=\"gray\">";
   
   public HtmlButton() {
      super();  
   }
   
   public HtmlButton(String text) {
      super(text);  
   }

   public void setEnabled( boolean b ) {
      if ( getText().startsWith( html ) ) {
         String text = getText();
         text = text.substring( html.length() );
         if ( b ) {
            if ( text.startsWith( disabledColor ) ) {
               text = text.substring( disabledColor.length() );
               text = html + text;
            }
         }
         else {
            text = html + disabledColor + text;
         }
         setText( text );
      }
      super.setEnabled( b );
   }
}


Submitted On 07-MAY-2004
Okke.Harsta
HtmlWindowButtonUI extends WindowsButtonUI {
	private final static HtmlWindowButtonUI 
buttonUI = new HtmlWindowButtonUI ();

    public static ComponentUI createUI(JComponent c) {
		return buttonUI;
	}

	public void paint(Graphics g, JComponent 
c) {
		super.paint(g, c);
		if (c.isEnabled()) {
			c.setForeground
(someStaticConfiguredColor); //Color.BLACK
		}
		else {
			c.setForeground
(someStaticConfiguredColor); //Color.GRAY
		}
	}


Submitted On 14-JUL-2004
OoPee
I like daleanson's helper class, but found you need to replace the "if(b)" block in setEnabled() by this:
     if ( b ) {
        if ( text.startsWith( disabledColor ) ) {
           text = text.substring( disabledColor.length() );
        }
        text = html + text;
     }
Otherwise, enabling an already enabled button removes
the HTML tag.


Submitted On 27-JUL-2004
jimhardwicktsi
In order to correctly handle multiple calls to setEnable(), change the class as follows:

import javax.swing.*;
public class HtmlButton extends JButton {
  private String html = "<html>";
  private String disabledColor = "<font color=\"gray\">";

  public HtmlButton() {
    super();
  }

  public HtmlButton(String text) {
    super(text);
  }

  public void setEnabled(boolean b) {
    if(getText().startsWith(html)) {
      String text = getText();
      text = text.substring(html.length());
      if(text.startsWith(disabledColor)) {
        text = text.substring(disabledColor.length());
      }

      if(b) {
        text = html + text;
      } else {
        text = html + disabledColor + text;
      }
      setText(text);
    }
    super.setEnabled(b);
  }
}

You can also extend a JToggleButton with this class if you'd like the disabled text on a toggle button.


Submitted On 14-MAR-2005
weiming_kang
There is a question about font where setText() on a jLabel : two charactors have lap over. Here is the part of  resource:
<html>
webcom<font color=red>puting</font>
</html>
if i do it , charactor "m" and charactor "p" will lap over partially


Submitted On 27-OCT-2005
AKNet
I changed a little workaround to maximize performance:

public class HtmlButton2 extends JButton {
    private String html = "<html>";
    private String disabledColor = "<font color=\"gray\">";

    String enabledText;
    String disabledText;
    boolean isHtml;

    public HtmlButton2() {
        super();
    }

    public HtmlButton2(String text) {
        super(text);
    }

    public void setText(String text) {
        if (text.startsWith(html)) {
            isHtml = true;
            enabledText = text;
            text = text.substring(html.length());
            disabledText = html + disabledColor + text;

            if (isEnabled()) {
                setTextImpl(enabledText);
            }
            else {
                setTextImpl(disabledText);
            }
        }
        else {
            isHtml = false;
            enabledText = text;
            disabledText = text;
            setTextImpl(enabledText);
        }
    }

    protected void setTextImpl(String text) {
        super.setText(text);
    }

    public void setEnabled(boolean b) {
        if (isHtml) {
            if (b) {
                setTextImpl(enabledText);
            }
            else {
                setTextImpl(disabledText);
            }
        }
        super.setEnabled(b);
    }

    public static void main(String[] args) {
        final HtmlButton2 hb2 = new HtmlButton2();
        final HtmlButton2 hb3 = new HtmlButton2();

        hb2.setText("<html> one <br> two </html>");
        hb3.setText("<html> two <br> two </html>");

        hb2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                hb3.setEnabled(true);
                hb2.setEnabled(false);
            }
        });
        hb3.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                hb2.setEnabled(true);
                hb3.setEnabled(false);
            }
        });

        JFrame frame = new JFrame();
        frame.getContentPane().add(hb2, BorderLayout.WEST);
        frame.getContentPane().add(hb3, BorderLayout.EAST);
        frame.pack();
        frame.show();
    }
}


Submitted On 08-NOV-2005
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.sun.java.swing.plaf.windows.WindowsButtonUI;
import java.awt.image.BufferedImage;

public class WindowsMLButtonUI
	extends WindowsButtonUI
	implements SwingConstants
{
	// Shared UI object
	private final static WindowsMLButtonUI buttonUI = new WindowsMLButtonUI();

	private JComponent dummy = new JButton();

	private WindowsMLButtonUI() {
		super();
   }

	// ********************************
	//          Create PLAF
	// ********************************
	public static ComponentUI createUI(JComponent c)
	{
		return buttonUI;
	}

	/* These rectangles/insets are allocated once for all
	 * ButtonUI.paint() calls.  Re-using rectangles rather than
	 * allocating them in each paint call substantially reduced the time
	 * it took paint to run.  Obviously, this method can't be re-entered.
	 */
	private static Rectangle viewRect = new Rectangle();
	private static Rectangle textRect = new Rectangle();
	private static Rectangle iconRect = new Rectangle();

	// ********************************
	//          Paint Methods
	// ********************************

	public void paint(Graphics g, JComponent c)
	{
		AbstractButton b = (AbstractButton) c;
		ButtonModel model = b.getModel();

		FontMetrics fm = g.getFontMetrics();

		Insets i = c.getInsets();

		viewRect.x = i.left;
		viewRect.y = i.top;
		viewRect.width = b.getWidth() - (i.right + viewRect.x);
		viewRect.height = b.getHeight() - (i.bottom + viewRect.y);

		textRect.x = textRect.y = textRect.width = textRect.height = 0;
		iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;

		Font f = c.getFont();
		g.setFont(f);

		// layout the text and icon
		String text = SwingUtilities.layoutCompoundLabel(
			c, fm, b.getText(), b.getIcon(),
			b.getVerticalAlignment(), b.getHorizontalAlignment(),
			b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
			viewRect, iconRect, textRect,
			b.getText() == null ? 0 : b.getIconTextGap());

		clearTextShiftOffset();

		// perform UI specific press action, e.g. Windows L&F shifts text
		if (model.isArmed() && model.isPressed())
		{
			paintButtonPressed(g, b);
		}

		// Paint the Icon
		if (b.getIcon() != null)
		{
			paintIcon(g, c, iconRect);
		}

		if (text != null && !text.equals(""))
		{
			View v = (View) c.getClientProperty(BasicHTML.propertyKey);
			if (v != null)
			{
				if (model.isEnabled())
				{
					v.paint(g, textRect);
				}
				else
				{
					// Draw the standard color html into a dummy graphic
					// needed as this calculates if there is enough room for the text and resizes
					// the button if there is not enough room.
					Image dummyImg = b.createImage(1,1);
					Graphics gDummy = dummyImg.getGraphics();
					gDummy.setClip(0,0,1,1);
					v.paint(gDummy, textRect);
					
					Color color  = UIManager.getColor("Button.disabledForeground");
				    Color shadow = UIManager.getColor("Button.disabledShadow");

                    // now draw the html in a brighter highlight
					dummy.setForeground(shadow);
					View disabledView = BasicHTML.createHTMLView(dummy, b.getText());
					disabledView.paint(g, textRect);

                    // now draw the html in a darker disabled colour one pixel offset from the
					// highlight
					g.translate( -1, -1);
					dummy.setForeground(color);
					disabledView = BasicHTML.createHTMLView(dummy, b.getText());
					disabledView.paint(g, textRect);
				}
			}
			else
			{
				// Plain text button
				super.paint(g,c);
			}
		}

		if (b.isFocusPainted() && b.hasFocus())
		{
			// paint UI specific focus
			paintFocus(g, b, viewRect, textRect, iconRect);
		}
	}
}

then create a button:

button.setUI((WindowsMLButtonUI)WindowsMLButtonUI.createUI(test));


Submitted On 15-MAR-2006
MichaelScheper
One point that most of you are overlooking is that inactive text is not always grey, or even 'gray'. Here's some code to make your solutions less platform-dependent:

private static String to2CharHexString(int i) {
	byte b = (byte) i;
	if (b > 15) {
		return Integer.toHexString(b);
	}
	return "0" + Integer.toHexString(b);
}

	String enabledHTML = "<font color=\"#" + to2CharHexString(SystemColor.textText.getRed()) + to2CharHexString(SystemColor.textText.getGreen()) + to2CharHexString(SystemColor.textText.getBlue()) + "\">";
	String disabledHTML = "<font color=\"#" + to2CharHexString(SystemColor.textInactiveText.getRed()) + to2CharHexString(SystemColor.textInactiveText.getGreen()) + to2CharHexString(SystemColor.textInactiveText.getBlue()) + "\">";

Then just use those strings instead of the hard-coded <color> tags you're using.


Submitted On 18-JUL-2006
A solution I've found to work:

btn.setForeground(SystemColor.textInactiveText);

directly before/after your call to setEnable, and:

btn.setForeground(SystemColor.textText);

directly before/after your subsequent call.  I've found it to have the exact same effect (And then some, for platform/skin independency) as merely adding <font color="gray"> right after <html>.

-Tianon


Submitted On 18-JUL-2006
Class implementing my work-around:

public class TButton extends JButton {
	public void setEnabled(boolean b) {
		if(getText().startsWith("<html>")) {
			if(b)
				setForeground(SystemColor.textText);
			else
				setForeground(SystemColor.textInactiveText);
		}
		super.setEnabled(b);
	}
}

-Tianon


Submitted On 18-JUL-2006
About that class, you'll have to add the constructors you need yourself.  Java isn't grabbing them automagically.  (Forgot about that...)

My apologies,
-Tianon


Submitted On 09-JUN-2007
sad8c7wefv78
Tianon, unfortunately your fix looks bad on Windows.


Submitted On 26-JUN-2008
andrew_krieg
Both the disabledForeground and the disabledShadow should be updated when disabling components using HTML tags in their text.  Not having this feature makes pluggable L&F sort of useless if you have to  manually override these properties for every component type that displays text.


Submitted On 26-JUN-2008
fix the problem in java side


Submitted On 26-JUN-2008
fix the problem in java


Submitted On 26-JUN-2008
fix the problem in java side


Submitted On 19-AUG-2008
bencole
This is sort of like Tianon's method, but it queries the current L&F for the active & inactive foreground defaults:
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.UIManager;

public class Label extends JLabel {
	//constructors & other methods...

	public void setEnabled(boolean b) {
		if (b == isEnabled()) return;
		
		if (getText().startsWith("<html>")) {
			setForeground(b ? (Color) UIManager.getDefaults().get("Label.foreground")
					: (Color) UIManager.getDefaults().get("Label.disabledForeground"));
		}
		
		super.setEnabled(b);
	}
}

or, for a JButton you could use (I believe):
setForeground(b ? (Color) UIManager.getDefaults().get("Button.foreground")
		: (Color) UIManager.getDefaults().get("Button.disabledText"));


Submitted On 17-SEP-2008
girikm
since html is used with swing components, the pirority could be increased.


Submitted On 17-OCT-2008
Barend
Here's my take on a work-around, nicely self-contained.


/**
 * Attaches to a JButton to work around Sun bug 4783068.
 * <p>http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4783068</p>
 */
public final class SunBug4783068Fixer implements PropertyChangeListener {
    
    private static final SunBug4783068Fixer INSTANCE = new SunBug4783068Fixer();
    
    public static void attach(AbstractButton to) {
        // Prevents adding it more than once to any single component.
        to.removePropertyChangeListener(INSTANCE);
        
        to.addPropertyChangeListener(INSTANCE);
    }
   
    public static void remove(AbstractButton from) {
        from.removePropertyChangeListener(INSTANCE);
    }
   
    public void propertyChange(PropertyChangeEvent evt) {
        if ((evt.getSource() instanceof AbstractButton)
             && "enabled".equals(evt.getPropertyName())) {
            AbstractButton target = (AbstractButton) evt.getSource();
            target.setForeground(target.isEnabled()
                ? (Color) UIManager.getDefaults().get("Button.foreground")
                : (Color) UIManager.getDefaults().get("Button.disabledText"));
        }
    }
}


Submitted On 06-FEB-2009
lisc
Barend 's solution is pretty good!



PLEASE NOTE: JDK6 is formerly known as Project Mustang