Index: /trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(revision 13110)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(revision 13111)
@@ -5,4 +5,5 @@
 import static org.openstreetmap.josm.tools.I18n.trn;
 
+import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Graphics2D;
@@ -18,9 +19,12 @@
 
 import javax.swing.Action;
+import javax.swing.BorderFactory;
 import javax.swing.Icon;
 import javax.swing.ImageIcon;
-import javax.swing.JToolTip;
+import javax.swing.JWindow;
 import javax.swing.SwingUtilities;
-
+import javax.swing.UIManager;
+
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.SaveActionBase;
 import org.openstreetmap.josm.data.Bounds;
@@ -32,4 +36,5 @@
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MainFrame;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
@@ -40,4 +45,5 @@
 import org.openstreetmap.josm.gui.io.importexport.NoteExporter;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.XmlWriter;
@@ -55,4 +61,8 @@
 
     private final NoteData noteData;
+
+    private Note displayedNote;
+    private HtmlPanel displayedPanel;
+    private JWindow displayedWindow;
 
     /**
@@ -81,4 +91,5 @@
         MainApplication.getMap().mapView.removeMouseListener(this);
         noteData.removeNoteDataUpdateListener(this);
+        hideNoteWindow();
         super.destroy();
     }
@@ -137,58 +148,102 @@
             g.drawImage(icon.getImage(), p.x - (width / 2), p.y - height, MainApplication.getMap().mapView);
         }
-        if (noteData.getSelectedNote() != null) {
-            StringBuilder sb = new StringBuilder("<html>");
-            sb.append(tr("Note"))
-              .append(' ').append(noteData.getSelectedNote().getId());
-            for (NoteComment comment : noteData.getSelectedNote().getComments()) {
-                String commentText = comment.getText();
-                //closing a note creates an empty comment that we don't want to show
-                if (commentText != null && !commentText.trim().isEmpty()) {
-                    sb.append("<hr/>");
-                    String userName = XmlWriter.encode(comment.getUser().getName());
-                    if (userName == null || userName.trim().isEmpty()) {
-                        userName = "&lt;Anonymous&gt;";
-                    }
-                    sb.append(userName);
-                    sb.append(" on ");
-                    sb.append(DateUtils.getDateFormat(DateFormat.MEDIUM).format(comment.getCommentTimestamp()));
-                    sb.append(":<br/>");
-                    String htmlText = XmlWriter.encode(comment.getText(), true);
-                    htmlText = htmlText.replace("&#xA;", "<br/>"); //encode method leaves us with entity instead of \n
-                    htmlText = htmlText.replace("/", "/\u200b"); //zero width space to wrap long URLs (see #10864)
-                    sb.append(htmlText);
+        Note selectedNote = noteData.getSelectedNote();
+        if (selectedNote != null) {
+            paintSelectedNote(g, mv, iconHeight, iconWidth, selectedNote);
+        } else {
+            hideNoteWindow();
+        }
+    }
+
+    private void hideNoteWindow() {
+        if (displayedWindow != null) {
+            displayedWindow.setVisible(false);
+            displayedWindow.dispose();
+            displayedWindow = null;
+            displayedPanel = null;
+            displayedNote = null;
+        }
+    }
+
+    private void paintSelectedNote(Graphics2D g, MapView mv, final int iconHeight, final int iconWidth, Note selectedNote) {
+        Point p = mv.getPoint(selectedNote.getLatLon());
+
+        g.setColor(ColorHelper.html2color(Config.getPref().get("color.selected")));
+        g.drawRect(p.x - (iconWidth / 2), p.y - iconHeight, iconWidth - 1, iconHeight - 1);
+
+        if (displayedNote != null && !displayedNote.equals(selectedNote)) {
+            hideNoteWindow();
+        }
+
+        Point screenloc = mv.getLocationOnScreen();
+        int tx = screenloc.x + p.x + (iconWidth / 2) + 5;
+        int ty = screenloc.y + p.y - iconHeight - 1;
+
+        String text = getNoteToolTip(selectedNote);
+
+        if (displayedWindow == null) {
+            displayedPanel = new HtmlPanel(text);
+            displayedPanel.setBackground(UIManager.getColor("ToolTip.background"));
+            displayedPanel.setForeground(UIManager.getColor("ToolTip.foreground"));
+            displayedPanel.setFont(UIManager.getFont("ToolTip.font"));
+            displayedPanel.setBorder(BorderFactory.createLineBorder(Color.black));
+            displayedPanel.enableClickableHyperlinks();
+            fixPanelSize(mv, text);
+            displayedWindow = new JWindow((MainFrame) Main.parent);
+            displayedWindow.add(displayedPanel);
+        } else {
+            displayedPanel.setText(text);
+            fixPanelSize(mv, text);
+        }
+
+        displayedWindow.pack();
+        displayedWindow.setLocation(tx, ty);
+        displayedWindow.setVisible(true);
+        displayedNote = selectedNote;
+    }
+
+    private void fixPanelSize(MapView mv, String text) {
+        Dimension d = displayedPanel.getPreferredSize();
+        if (d.width > mv.getWidth() / 2) {
+            // To make sure long notes such as https://www.openstreetmap.org/note/278197 are displayed correctly
+            displayedPanel.setText(text.replaceAll("\\. ([\\p{Lower}\\p{Upper}\\p{Punct}])", "\\.<br>$1"));
+        }
+    }
+
+    /**
+     * Returns the HTML-formatted tooltip text for the given note.
+     * @param note note to display
+     * @return the HTML-formatted tooltip text for the given note
+     * @since 13111
+     */
+    public static String getNoteToolTip(Note note) {
+        StringBuilder sb = new StringBuilder("<html>");
+        sb.append(tr("Note"))
+          .append(' ').append(note.getId());
+        for (NoteComment comment : note.getComments()) {
+            String commentText = comment.getText();
+            //closing a note creates an empty comment that we don't want to show
+            if (commentText != null && !commentText.trim().isEmpty()) {
+                sb.append("<hr/>");
+                String userName = XmlWriter.encode(comment.getUser().getName());
+                if (userName == null || userName.trim().isEmpty()) {
+                    userName = "&lt;Anonymous&gt;";
                 }
+                sb.append(userName)
+                  .append(" on ")
+                  .append(DateUtils.getDateFormat(DateFormat.MEDIUM).format(comment.getCommentTimestamp()))
+                  .append(":<br>");
+                String htmlText = XmlWriter.encode(comment.getText(), true);
+                // encode method leaves us with entity instead of \n
+                htmlText = htmlText.replace("&#xA;", "<br>");
+                // convert URLs to proper HTML links
+                htmlText = htmlText.replaceAll("(https?://\\S+)", "<a href=\"$1\">$1</a>");
+                sb.append(htmlText);
             }
-            sb.append("</html>");
-            JToolTip toolTip = new JToolTip();
-            toolTip.setTipText(sb.toString());
-            Point p = mv.getPoint(noteData.getSelectedNote().getLatLon());
-
-            g.setColor(ColorHelper.html2color(Config.getPref().get("color.selected")));
-            g.drawRect(p.x - (iconWidth / 2), p.y - iconHeight,
-                    iconWidth - 1, iconHeight - 1);
-
-            int tx = p.x + (iconWidth / 2) + 5;
-            int ty = p.y - iconHeight - 1;
-            g.translate(tx, ty);
-
-            //Carried over from the OSB plugin. Not entirely sure why it is needed
-            //but without it, the tooltip doesn't get sized correctly
-            for (int x = 0; x < 2; x++) {
-                Dimension d = toolTip.getUI().getPreferredSize(toolTip);
-                d.width = Math.min(d.width, mv.getWidth() / 2);
-                if (d.width > 0 && d.height > 0) {
-                    toolTip.setSize(d);
-                    try {
-                        toolTip.paint(g);
-                    } catch (IllegalArgumentException e) {
-                        // See #11123 - https://bugs.openjdk.java.net/browse/JDK-6719550
-                        // Ignore the exception, as Netbeans does: http://hg.netbeans.org/main-silver/rev/c96f4d5fbd20
-                        Logging.log(Logging.LEVEL_ERROR, e);
-                    }
-                }
-            }
-            g.translate(-tx, -ty);
-        }
+        }
+        sb.append("</html>");
+        String result = sb.toString();
+        Logging.debug(result);
+        return result;
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java	(revision 13110)
+++ /trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java	(revision 13111)
@@ -35,6 +35,4 @@
 import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
-import javax.swing.event.HyperlinkEvent;
-import javax.swing.event.HyperlinkListener;
 import javax.swing.text.html.HTMLEditorKit;
 
@@ -52,5 +50,4 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
-import org.openstreetmap.josm.tools.OpenBrowser;
 import org.openstreetmap.josm.tools.UserCancelException;
 import org.openstreetmap.josm.tools.Utils;
@@ -137,5 +134,5 @@
                         + "</body></html>"
         );
-        pnlMessage.getEditorPane().addHyperlinkListener(new ExternalBrowserLauncher());
+        pnlMessage.enableClickableHyperlinks();
         pnl.add(pnlMessage, gc);
 
@@ -423,12 +420,3 @@
         }
     }
-
-    static class ExternalBrowserLauncher implements HyperlinkListener {
-        @Override
-        public void hyperlinkUpdate(HyperlinkEvent e) {
-            if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
-                OpenBrowser.displayUrl(e.getDescription());
-            }
-        }
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 13110)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 13111)
@@ -39,5 +39,4 @@
 import javax.swing.JToolBar;
 import javax.swing.UIManager;
-import javax.swing.event.HyperlinkEvent.EventType;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -74,5 +73,4 @@
 import org.openstreetmap.josm.tools.LanguageInfo;
 import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.OpenBrowser;
 
 /**
@@ -371,9 +369,5 @@
             HtmlPanel help = new HtmlPanel(tr("New default entries can be added in the <a href=\"{0}\">Wiki</a>.",
                 Main.getJOSMWebsite()+"/wiki/Maps"));
-            help.getEditorPane().addHyperlinkListener(e -> {
-                if (e.getEventType() == EventType.ACTIVATED) {
-                    OpenBrowser.displayUrl(e.getURL().toString());
-                }
-            });
+            help.enableClickableHyperlinks();
             add(help, GBC.eol().insets(10, 0, 0, 10).fill(GBC.HORIZONTAL));
 
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java	(revision 13110)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java	(revision 13111)
@@ -15,10 +15,8 @@
 import javax.swing.SwingConstants;
 import javax.swing.SwingUtilities;
-import javax.swing.event.HyperlinkEvent.EventType;
 
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
 import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.tools.OpenBrowser;
 
 /**
@@ -157,9 +155,5 @@
             HtmlPanel description = new HtmlPanel();
             description.setText(pi.getDescriptionAsHtml());
-            description.getEditorPane().addHyperlinkListener(e -> {
-                if (e.getEventType() == EventType.ACTIVATED) {
-                    OpenBrowser.displayUrl(e.getURL().toString());
-                }
-            });
+            description.enableClickableHyperlinks();
             lblPlugin.setLabelFor(description);
 
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/HtmlPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/HtmlPanel.java	(revision 13110)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/HtmlPanel.java	(revision 13111)
@@ -5,4 +5,5 @@
 import java.awt.Font;
 import java.text.MessageFormat;
+import java.util.Arrays;
 import java.util.Optional;
 
@@ -10,5 +11,9 @@
 import javax.swing.JPanel;
 import javax.swing.UIManager;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
 import javax.swing.text.html.StyleSheet;
+
+import org.openstreetmap.josm.tools.OpenBrowser;
 
 /**
@@ -22,4 +27,11 @@
  */
 public class HtmlPanel extends JPanel {
+
+    private static final HyperlinkListener defaultHyperlinkListener = e -> {
+        if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
+            OpenBrowser.displayUrl(e.getURL().toString());
+        }
+    };
+
     private JosmEditorPane jepMessage;
 
@@ -88,3 +100,13 @@
         jepMessage.setText(Optional.ofNullable(text).orElse(""));
     }
+
+    /**
+     * Opens hyperlinks on click.
+     * @since 13111
+     */
+    public final void enableClickableHyperlinks() {
+        if (!Arrays.asList(jepMessage.getHyperlinkListeners()).contains(defaultHyperlinkListener)) {
+            jepMessage.addHyperlinkListener(defaultHyperlinkListener);
+        }
+    }
 }
