Search code examples
javahtmlswingawtjtextpane

Java Swing JTextPane text/html not honoring disabled attribute


I am trying to display a simple HTML page in a JTextPane. The page has input checkbox/radio elements but they need to be disabled. However, when the page is displayed, the checkbox and radio buttons are still enabled. I don't want the user to be able to click and change their state. Is this a bug or I am doing something wrong. Please help. Here is sample code:

import javax.swing.*;
public class JEPTest {
    public static void main(String[] args) throws Exception{
        String data = "<html>\n" +
"\n" +
"<body>\n" +
"<input type='radio' disabled>\n" +
"\n" +
"</body>\n" +
"</html>";

    final JEditorPane textPane = new JEditorPane();
        textPane.setContentType("text/html");
        textPane.setEnabled(false);
        JScrollPane paneScrollPane = new JScrollPane(textPane);
        paneScrollPane.setVerticalScrollBarPolicy(
                        JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        paneScrollPane.setHorizontalScrollBarPolicy(
                        JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        //paneScrollPane.setPreferredSize(new Dimension(250, 155));
        paneScrollPane.setMinimumSize(new Dimension(100, 100));        


    JButton jb = new JButton("set");
    jb.addActionListener
        (
        new ActionListener()
    {
        public void actionPerformed(ActionEvent ae)
        {
        textPane.setText(data);

        }
    }
        );

    JFrame jf = new JFrame();
    jf.setSize(800, 600);       

    jf.getContentPane().add(textPane, BorderLayout.CENTER);
    jf.getContentPane().add(jb, BorderLayout.SOUTH);
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jf.setVisible(true);

    }
}

Solution

  • Swing uses an old HTML rendering engine which only supports HTML 3.2. The HTML 3.2 specification reveals that the <input> element did not allow for a disabled attribute at the time of that version. The defined attributes were:

    <!ATTLIST INPUT
            type %InputType TEXT     -- what kind of widget is needed --
            name  CDATA #IMPLIED     -- required for all but submit and reset --
            value CDATA #IMPLIED     -- required for radio and checkboxes --
            checked (checked) #IMPLIED -- for radio buttons and check boxes --
            size CDATA  #IMPLIED     -- specific to each type of field --
            maxlength NUMBER #IMPLIED
            src   %URL  #IMPLIED     -- for fields with background images --
            align %IAlign #IMPLIED   -- vertical or horizontal alignment --
            >
    

    The best workaround is to avoid using JEditorPane and just create a JCheckBox instance in a JPanel.

    Alternatively, you can embed any visual Java bean, including all JComponent descendants, in a JEditorPane’s HTML document, using an <object> element. This is described in the javax.swing.text.html.ObjectView documentation.

    You can use this capability to embed a JRadioButton directly. However, only String properties can be set this way, so you will have to create your own subclass of JRadioButton and add a String property which wraps the enabled property:

    public class JEPTest {
        public static class StringPropRadioButton
        extends JRadioButton {
            private static final long serialVersionUID = 1;
    
            public String getEnabledAsString() {
                return String.valueOf(isEnabled());
            }
    
            public void setEnabledAsString(String enabled) {
                setEnabled(Boolean.parseBoolean(enabled));
            }
        }
    
        public static void main(String[] args) throws Exception{
            String data = "<html>\n" +
            "\n" +
            "<body>\n" +
            "<input type='radio' disabled>\n" +
            "\n" +
            "<p>" +
                "<object classid='JEPTest$StringPropRadioButton' id='option1'>" +
                "    <param name='text' value='Option 1'>" +
                "    <param name='enabledAsString' value='false'>" +
                "</object>" +
            "</body>\n" +
            "</html>";