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