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