Java Swing CSS style attribute “class” use in HTML

2019-08-28 20:03发布

my problem is as follows:

I'm trying to add functionality to an HTML editor which is implemented in Java Swing using HTMLEditorKit and HTMLDocument classes. The idea is to provide a possibility to switch between different styles during editing of the text in the HTML editor. The different styles define background/foreground colors, fonts and the like.

I already managed to load a stylesheet file programmatically. The code for that looks as follows:

class HtmlEditor extends JFrame implements Keylistener, MouseListener {

private HTMLDocument  m_doc;

private HTMLEditorKit m_kit;

[...]


public HtmlEditor(...) {

    [...]
    final URL formats = HtmlEditor.class.getResource("/formats.css");
predefStyles = new StyleSheet();
predefStyles.importStyleSheet(formats);
m_kit.getStyleSheet().addStyleSheet(predefStyles);

    [...]
}

The style sheet file looks as follows:

.style1 { background-color:silver }
.style2 { background-color:aqua }
.style3 { background-color:teal }

With that style sheet added to the HTMLEditorKit instance's StyleSheet I can already use the styles when inserting content into the HTML editor using its source editor (in the source editor one can insert HTML code directly, e.g. "<span class="style1">Styled text</span>"). To allow for use of the styles in the WYSIWYG part too I added a JComboBox into the Editor's toolbar which displays the names of the styles from the "formats.css" (the leading "." from the style names is removed). In the JComboBox's ActionListener I'm now trying to set the "class" attribute. My code looks as follows:

    cbStyles = new JComboBox<String>(getStyleNames());
    cbStyles.setToolTipText("Select Style");
    cbStyles.setMaximumSize(null);
    cbStyles.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            currStyle = cbStyles.getSelectedItem().toString();

            final SimpleAttributeSet attr = new SimpleAttributeSet();
            attr.addAttribute(HTML.Attribute.CLASS, currStyle);
            m_kit.getInputAttributes().addAttributes(attr);
            m_editor.requestFocusInWindow();
        }
    });
    m_toolBar.add(cbStyles);

Unfortunately this does not seem to function. At least, after changing the style I do not get it applied to the text I'm entering afterwards and in the source view the "class" attribute does not appear in the containing HTML component. I inspected the code in the HtmlEditor class that manages setting another font to see if I can use the same technique that is used for chaning the font for my purpose too. The respective action listener attached to the JComboBox that provides font selection looks as follows:

        cbFonts.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            m_fontName = m_cbFonts.getSelectedItem().toString();
            final MutableAttributeSet attr = new SimpleAttributeSet();
            StyleConstants.setFontFamily(attr, m_fontName);
            m_kit.getInputAttributes().addAttributes(attr);
            m_editor.requestFocusInWindow();
        }
    });

As we can see here the class StyleConstants is used to set the attribute to the new font. What's puzzling me is that the StyleConstants class does not seem to provide a method for setting a "class" attribute. As can be seen in its Javadoc there are numerous methods for setting attributes like alignment, font family, boldness, etc. Possibly my approach is incorrect in a quite simple way. Maybe I did not yet properly understand the concepts of using CSS in an HTMLDocument. Any help would be appreciated!

From my understanding in HTML CSS can be used as follows:

A <style> element inside the <head> part defines style classes. Example:

<style type="text/css">;
.style1 { background-color:silver }
.style2 { background-color:aqua }
.style3 { background-color:teal }
</style>

Then inside the HTML these classes can be referenced through the universal attribute "class" inside of HTML elements. Example:

<span class="style1">Text in style1</span>

So it should be possible somehow to insert the "class" attribute with a predefined style as its value into an element in an HTMLDocument that is managed by an HTMLEditorKit. My little HTML editor is to provide functionality to activate a style at a current caret position inside the document. So let's assume the caret position is inside a paragraph like this:

<p>Some text without style </p>

after the word "style" before the closing paragraph tag.

Now I want to activate, let's say, style1. This should then result in a structure like the following:

<p>Some text without style<span class="style1"> </span></p>

And the caret position now should be inside the new <span> element.

Or if caret were at the beginning of an empty paragraph:

<p></p>

after applying a style the structure should look as follows:

<p class="style1"> </p>

I assume that this should be no big deal but have no clue how to get this done.

In between I tried another way to solve my problem but with no success. Here comes the complete example code (hopefully not too much;-):

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.StringWriter;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.border.EmptyBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;

public class HTMLEditor extends JFrame {

private static final long serialVersionUID = 1L;
private final JEditorPane jep;
private final HTMLEditorKit edtKit;
private final HTMLDocument doc;

public static void main(String[] args) {
    final HTMLEditor editor = new HTMLEditor();
    editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    editor.setVisible(true);
}

public HTMLEditor() {
    super("Simple HTML Editor");
    jep = new JEditorPane();
    edtKit = new HTMLEditorKit();
    jep.setEditorKit(edtKit);
    addMyStyles();

    doc = (HTMLDocument) edtKit.createDefaultDocument();
    jep.setDocument(doc);
    jep.setText("<html><head></head>" +
            "<body><div class=\"style1\">STYLE1</div>" +
            "</body>");

    final Container content = getContentPane();
    content.add(jep, BorderLayout.CENTER);
    content.add(createToolBar(), BorderLayout.NORTH);
    setJMenuBar(createMenuBar());
    setSize(320, 240);
}

/**
 * Adds three simple style definitions to the HTMLEditorKit's style sheet.
 */
void addMyStyles() {
    final StyleSheet styles = edtKit.getStyleSheet();
    styles.addRule(".style1 { background-color:silver; }");
    styles.addRule(".style2 { background-color:aqua; }");
    styles.addRule(".style3 { background-color:teal; }");
}

/**
 * Creates the toolbar with two buttons:
 * <li>Button to switch between bold and normal text
 * <li>Button for activating a style class
 * @return The toolbar
 */
protected JToolBar createToolBar() {
    final JToolBar bar = new JToolBar();
    final Action boldAct = jep.getActionMap().get("font-bold");
    boldAct.putValue(Action.NAME, "Bold");
    bar.add(boldAct);
    bar.addSeparator();
    final Action styleAct = new StyleAction();
    jep.getActionMap().put("activate-style", styleAct);
    bar.add(styleAct);

    return bar;
}

/**
 * Creates the menu bar. It only offers and action to display the HTML source
 * in a popup window.
 * @return The menu bar
 */
protected JMenuBar createMenuBar() {
    final JMenuBar menubar = new JMenuBar();
    final JMenu view = new JMenu("View");
    menubar.add(view);
    final JMenuItem preview = new JMenuItem("Preview");
    view.add(preview);
    preview.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            final HTMLPreview previewer =
                    new HTMLPreview(HTMLEditor.this, getDocSource());
            previewer.setVisible(true);
        }
    });

    return menubar;
}

/**
 * Helper method to extract the HTML source code from the HTML document
 * @return The HTML source code
 */
private String getDocSource() {
    final StringWriter sw = new StringWriter();
    try {
        edtKit.write(sw, doc, 0, doc.getLength());
    } catch (IOException | BadLocationException e1) {
        e1.printStackTrace();
    }
    try {
        sw.close();
    } catch (final IOException e1) {
        e1.printStackTrace();
    }
    return sw.toString();
}

/**
 * Implements the action for insertion of a user supplied style (should be
 * "style1", "style2" or "style3").
 *
 */
public class StyleAction extends StyledEditorKit.StyledTextAction {

    private static final long serialVersionUID = 1L;

    public StyleAction() {
        super("Activate a Style");
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        final JEditorPane editor = getEditor(ae);
        if (editor == null)
            return;
        final String value = JOptionPane.showInputDialog(HTMLEditor.this,
                                                         "Style Name:");
        try {
            final String text = "<span class=\"" + value + "\"></span>";
            edtKit.insertHTML(doc, editor.getCaretPosition(),
                              text, 0, 0, HTML.Tag.SPAN);
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }
}
}

/**
 * Popup window for display of the current contents of the editor as HTML
 * source code.
 */
class HTMLPreview extends JDialog {

private static final long serialVersionUID = 1L;

public HTMLPreview(JFrame parent, String source) {
    super(parent, "HTML Source", true);

    final JPanel pp = new JPanel(new BorderLayout());
    pp.setBorder(new EmptyBorder(10, 10, 5, 10));

    final JTextArea srcTxtArea = new JTextArea(source, 20, 60);
    srcTxtArea.setFont(new Font("Courier", Font.PLAIN, 12));
    final JScrollPane sp = new JScrollPane(srcTxtArea);
    pp.add(sp, BorderLayout.CENTER);

    final JPanel p = new JPanel(new FlowLayout());
    final JPanel p1 = new JPanel(new GridLayout(1, 2, 10, 0));

    final JButton closeBtn = new JButton("Close");
    closeBtn.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            dispose();
        }
    });
    p1.add(closeBtn);
    p.add(p1);
    pp.add(p, BorderLayout.SOUTH);
    getContentPane().add(pp, BorderLayout.CENTER);
    pack();
    setResizable(true);
    setLocationRelativeTo(parent);
}
}

As can be seen in the ctor. of class HTMLEditor I first added three user defined styles (style[1-3]) and then checked that they work by inserting text that uses style1 for formatting. I was hoping that switching between user defined styles for formatting of text should be accomplished in a similar way as switching text between bold and normal font display. So my question is: How can I apply user defined CSS styles to elements inside an HTMLDocument? Thanks a lot for any hint.

0条回答
登录 后发表回答