001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import java.awt.Color;
005import java.awt.Font;
006import java.io.IOException;
007import java.io.InputStream;
008import java.net.URL;
009import java.text.MessageFormat;
010
011import javax.swing.JEditorPane;
012import javax.swing.LookAndFeel;
013import javax.swing.UIDefaults;
014import javax.swing.UIManager;
015import javax.swing.text.html.StyleSheet;
016
017import org.openstreetmap.josm.gui.util.GuiHelper;
018import org.openstreetmap.josm.tools.HttpClient;
019import org.openstreetmap.josm.tools.LanguageInfo;
020
021/**
022 * Subclass of {@link JEditorPane} that adds a "native" context menu (cut/copy/paste/select all)
023 * and effectively uses JOSM user agent when performing HTTP request in {@link #setPage(URL)} method.
024 * @since 5886
025 */
026public class JosmEditorPane extends JEditorPane {
027
028    /**
029     * Creates a new <code>JosmEditorPane</code>.
030     * The document model is set to <code>null</code>.
031     */
032    public JosmEditorPane() {
033        TextContextualPopupMenu.enableMenuFor(this, true);
034    }
035
036    /**
037     * Creates a <code>JosmEditorPane</code> based on a specified URL for input.
038     *
039     * @param initialPage the URL
040     * @throws IOException if the URL is <code>null</code> or cannot be accessed
041     */
042    public JosmEditorPane(URL initialPage) throws IOException {
043        this();
044        setPage(initialPage);
045    }
046
047    /**
048     * Creates a <code>JosmEditorPane</code> based on a string containing
049     * a URL specification.
050     *
051     * @param url the URL
052     * @throws IOException if the URL is <code>null</code> or cannot be accessed
053     */
054    public JosmEditorPane(String url) throws IOException {
055        this();
056        setPage(url);
057    }
058
059    /**
060     * Creates a <code>JosmEditorPane</code> that has been initialized
061     * to the given text.  This is a convenience constructor that calls the
062     * <code>setContentType</code> and <code>setText</code> methods.
063     *
064     * @param type mime type of the given text
065     * @param text the text to initialize with; may be <code>null</code>
066     * @throws NullPointerException if the <code>type</code> parameter
067     *      is <code>null</code>
068     */
069    public JosmEditorPane(String type, String text) {
070        this();
071        setContentType(type);
072        setText(text);
073    }
074
075    @Override
076    protected InputStream getStream(URL page) throws IOException {
077        final HttpClient.Response conn = HttpClient.create(page).connect();
078        String type = conn.getContentType();
079        if (type != null) {
080            setContentType(type);
081        }
082        return conn.getContent();
083    }
084
085    /**
086     * Adapts a {@link JEditorPane} to be used as a powerful replacement of {@link javax.swing.JLabel}.
087     * @param pane The editor pane to adapt
088     * @param allBold If {@code true}, makes all text to be displayed in bold
089     */
090    public static void makeJLabelLike(JEditorPane pane, boolean allBold) {
091        pane.setContentType("text/html");
092        pane.setOpaque(false);
093        pane.setEditable(false);
094        adaptForNimbus(pane);
095
096        JosmHTMLEditorKit kit = new JosmHTMLEditorKit();
097        final Font f = UIManager.getFont("Label.font");
098        final StyleSheet ss = new StyleSheet();
099        ss.addRule((allBold ? "html" : "strong, b") + " {" + getFontRule(f) + '}');
100        ss.addRule("a {text-decoration: underline; color: blue}");
101        ss.addRule("h1 {" + getFontRule(GuiHelper.getTitleFont()) + '}');
102        ss.addRule("ol {margin-left: 1cm; margin-top: 0.1cm; margin-bottom: 0.2cm; list-style-type: decimal}");
103        ss.addRule("ul {margin-left: 1cm; margin-top: 0.1cm; margin-bottom: 0.2cm; list-style-type: disc}");
104        if ("km".equals(LanguageInfo.getJOSMLocaleCode())) {
105            // Fix rendering problem for Khmer script
106            ss.addRule("p {" + getFontRule(UIManager.getFont("Label.font")) + '}');
107        }
108        kit.setStyleSheet(ss);
109        pane.setEditorKit(kit);
110    }
111
112    /**
113     * Adapts a {@link JEditorPane} for Nimbus look and feel.
114     * See <a href="https://stackoverflow.com/q/15228336/2257172">this StackOverflow question</a>.
115     * @param pane The editor pane to adapt
116     * @since 6935
117     */
118    public static void adaptForNimbus(JEditorPane pane) {
119        LookAndFeel currentLAF = UIManager.getLookAndFeel();
120        if (currentLAF != null && "Nimbus".equals(currentLAF.getName())) {
121            Color bgColor = UIManager.getColor("Label.background");
122            UIDefaults defaults = new UIDefaults();
123            defaults.put("EditorPane[Enabled].backgroundPainter", bgColor);
124            pane.putClientProperty("Nimbus.Overrides", defaults);
125            pane.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
126            pane.setBackground(bgColor);
127        }
128    }
129
130    private static String getFontRule(Font f) {
131        return MessageFormat.format(
132                "font-family: ''{0}'';font-size: {1,number}pt; font-weight: {2}; font-style: {3}",
133                f.getName(),
134                f.getSize(),
135                "bold",
136                f.isItalic() ? "italic" : "normal"
137        );
138    }
139}