001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import java.awt.BorderLayout;
005import java.awt.Font;
006import java.text.MessageFormat;
007import java.util.Arrays;
008import java.util.Optional;
009
010import javax.swing.JEditorPane;
011import javax.swing.JPanel;
012import javax.swing.UIManager;
013import javax.swing.event.HyperlinkEvent;
014import javax.swing.event.HyperlinkListener;
015import javax.swing.text.html.StyleSheet;
016
017import org.openstreetmap.josm.tools.OpenBrowser;
018
019/**
020 * This panel can be used to display larger sections of formatted text in
021 * HTML.
022 *
023 * It displays HTML text in the same font as {@link javax.swing.JLabel}. Hyperlinks are rendered in
024 * blue and they are underlined. There is also a CSS rule for the HTML tag <strong>
025 * configured.
026 * @since 2688
027 */
028public class HtmlPanel extends JPanel {
029
030    private static final HyperlinkListener defaultHyperlinkListener = e -> {
031        if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
032            OpenBrowser.displayUrl(e.getURL().toString());
033        }
034    };
035
036    private JosmEditorPane jepMessage;
037
038    protected final void build() {
039        setLayout(new BorderLayout());
040        jepMessage = new JosmEditorPane("text/html", "");
041        jepMessage.setOpaque(false);
042        jepMessage.setEditable(false);
043        Font f = UIManager.getFont("Label.font");
044        StyleSheet ss = new StyleSheet();
045        ss.addRule("body {" + MessageFormat.format(
046                "font-family: ''{0}'';font-size: {1,number}pt; font-weight: {2}; font-style: {3}",
047                f.getName(),
048                f.getSize(),
049                f.isBold() ? "bold" : "normal",
050                f.isItalic() ? "italic" : "normal"
051        ) + '}');
052        ss.addRule("strong {" + MessageFormat.format(
053                "font-family: ''{0}'';font-size: {1,number}pt; font-weight: {2}; font-style: {3}",
054                f.getName(),
055                f.getSize(),
056                "bold",
057                f.isItalic() ? "italic" : "normal"
058        ) + '}');
059        ss.addRule("a {text-decoration: underline; color: blue}");
060        ss.addRule("ul {margin-left: 1cm; list-style-type: disc}");
061        JosmHTMLEditorKit kit = new JosmHTMLEditorKit();
062        kit.setStyleSheet(ss);
063        jepMessage.setEditorKit(kit);
064
065        add(jepMessage, BorderLayout.CENTER);
066    }
067
068    /**
069     * Constructs a new {@code HtmlPanel}.
070     */
071    public HtmlPanel() {
072        build();
073    }
074
075    /**
076     * Constructs a new {@code HtmlPanel} with the given HTML text.
077     * @param text the text to display
078     */
079    public HtmlPanel(String text) {
080        this();
081        setText(text);
082    }
083
084    /**
085     * Replies the editor pane used internally to render the HTML text.
086     *
087     * @return the editor pane used internally to render the HTML text.
088     */
089    public JEditorPane getEditorPane() {
090        return jepMessage;
091    }
092
093    /**
094     * Sets the current text to display. <code>text</code> is a html fragment.
095     * If null, empty string is assumed.
096     *
097     * @param text the text to display
098     */
099    public final void setText(String text) {
100        jepMessage.setText(Optional.ofNullable(text).orElse(""));
101    }
102
103    /**
104     * Opens hyperlinks on click.
105     * @since 13111
106     */
107    public final void enableClickableHyperlinks() {
108        if (!Arrays.asList(jepMessage.getHyperlinkListeners()).contains(defaultHyperlinkListener)) {
109            jepMessage.addHyperlinkListener(defaultHyperlinkListener);
110        }
111    }
112}