Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8202529

No HyperlinkEvent is fired when mouse leaves hyperlink and JEditorPane component

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Linux 4.8.0-53-generic #56~16.04.1-Ubuntu SMP Tue May 16 01:18:56 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

      A DESCRIPTION OF THE PROBLEM :
      When mouse is moved over a hyperlink in a JEditorPane component and then leaves both the hyperlink and the component area, javax.swing.event.HyperlinkListener#hyperlinkUpdate is not called for listeners registered on the component.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the attached HyperlinkListenerExample class.
      Move the mouse horizontally throughout the text "Move mouse over the link horizontally, then vertically".
      Output will contain as expected:
      hyperlinkUpdate for url: my-link, eventType: ENTERED
      hyperlinkUpdate for url: my-link, eventType: EXITED

      Now move the mouse vertically throughout the underlined link (i.e. "link" word).
      Actual output:
      hyperlinkUpdate for url: my-link, eventType: ENTERED

      Expected output:
      hyperlinkUpdate for url: my-link, eventType: ENTERED
      hyperlinkUpdate for url: my-link, eventType: EXITED

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      When mouse enters a hyperlink and then leaves the hyperlink and the component area, javax.swing.event.HyperlinkListener#hyperlinkUpdate is called with e.getEventType() == "EXITED".
      ACTUAL -
      No "EXITED" HyperlinkEvent is fired.

      ---------- BEGIN SOURCE ----------
      import javax.swing.*;
      import javax.swing.event.HyperlinkEvent;
      import javax.swing.event.HyperlinkListener;
      import javax.swing.text.html.HTMLEditorKit;
      import java.awt.*;
      import java.awt.event.MouseEvent;
      import java.awt.event.MouseListener;
      import java.awt.event.MouseMotionListener;

      public class HyperlinkListenerExample {

        private HyperlinkListenerExample() {
        }

        private JPanel createPanel() {
          JPanel panel = new JPanel(new BorderLayout());
          panel.add(Box.createVerticalStrut(100), BorderLayout.NORTH);
          JEditorPane pane = createPane();
          pane.setText("<html><body>Move mouse over the <a href='my-link'>link</a> horizontally, then vertically</body></html>");
          panel.add(pane, BorderLayout.CENTER);
          panel.add(Box.createVerticalStrut(100), BorderLayout.SOUTH);
          return panel;
        }

        private static JEditorPane createPane() {
          JEditorPane textPane = new JEditorPane();
          textPane.setEditorKit(new HTMLEditorKit());
          textPane.setEditable(false);
          textPane.setOpaque(false);
          textPane.setFocusable(false);
          textPane.setBorder(null);
          textPane.addHyperlinkListener(new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent e) {
              System.out.println("hyperlinkUpdate for url: " + e.getDescription() + ", eventType: " + e.getEventType());
            }
          });
          return textPane;
        }

        private static void createAndShowGUI() {
          JFrame frame = new JFrame("HyperlinkListenerExample");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

          frame.add(new HyperlinkListenerExample().createPanel());

          frame.pack();
          frame.setVisible(true);
          frame.setLocationRelativeTo(null);
        }

        public static void main(String[] args) {
          SwingUtilities.invokeLater(HyperlinkListenerExample::createAndShowGUI);
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      As a workaround, it's possible to extend javax.swing.text.html.HTMLEditorKit.LinkController and emulate mouseMoved event when mouse exits the component area:

      import javax.swing.*;
      import javax.swing.event.HyperlinkEvent;
      import javax.swing.event.HyperlinkListener;
      import javax.swing.text.html.HTMLEditorKit;
      import java.awt.*;
      import java.awt.event.MouseEvent;
      import java.awt.event.MouseListener;
      import java.awt.event.MouseMotionListener;

      public class WorkaroundHyperlinkListenerExample {

        private WorkaroundHyperlinkListenerExample() {
        }

        private JPanel createPanel() {
          JPanel panel = new JPanel(new BorderLayout());
          panel.add(Box.createVerticalStrut(100), BorderLayout.NORTH);
          JEditorPane pane = createPane();
          pane.setText("<html><body>Move mouse over the <a href='my-link'>link</a> horizontally, then vertically</body></html>");
          panel.add(pane, BorderLayout.CENTER);
          panel.add(Box.createVerticalStrut(100), BorderLayout.SOUTH);
          return panel;
        }

        private static JEditorPane createPane() {
          JEditorPane textPane = new JEditorPane();
          textPane.setEditorKit(new MyHTMLEditorKit());
          textPane.setEditable(false);
          textPane.setOpaque(false);
          textPane.setFocusable(false);
          textPane.setBorder(null);
          textPane.addHyperlinkListener(new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent e) {
              System.out.println("hyperlinkUpdate for url: " + e.getDescription() + ", eventType: " + e.getEventType());
            }
          });
          return textPane;
        }

        private static void createAndShowGUI() {
          JFrame frame = new JFrame("WorkaroundHyperlinkListenerExample");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

          frame.add(new WorkaroundHyperlinkListenerExample().createPanel());

          frame.pack();
          frame.setVisible(true);
          frame.setLocationRelativeTo(null);
        }

        public static class MyHTMLEditorKit extends HTMLEditorKit {

          public void install(JEditorPane c) {
            MouseListener[] oldMouseListeners = c.getMouseListeners();
            MouseMotionListener[] oldMouseMotionListeners = c.getMouseMotionListeners();
            super.install(c);

            for (MouseListener l : c.getMouseListeners()) {
              c.removeMouseListener(l);
            }
            for (MouseListener l : oldMouseListeners) {
              c.addMouseListener(l);
            }

            for (MouseMotionListener l : c.getMouseMotionListeners()) {
              c.removeMouseMotionListener(l);
            }
            for (MouseMotionListener l : oldMouseMotionListeners) {
              c.addMouseMotionListener(l);
            }

            MyHTMLEditorKit.MyLinkController handler = new MyHTMLEditorKit.MyLinkController();
            c.addMouseListener(handler);
            c.addMouseMotionListener(handler);
          }

          public class MyLinkController extends LinkController {
            @Override
            public void mouseExited(MouseEvent e) {
              mouseMoved(new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiersEx(), -1, -1, e.getClickCount(), e.isPopupTrigger(), e.getButton()));
            }
          }
        }

        public static void main(String[] args) {
          SwingUtilities.invokeLater(WorkaroundHyperlinkListenerExample::createAndShowGUI);
        }
      }

      FREQUENCY : always


            sveerabhadra Shashidhara Veerabhadraiah (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: