Jumping to internal anchors in JEditorPane
Asked Answered
C

2

5

I've got a problem: I want to use internal anchors <a name="x"> and links <a href="#x"> inside a JEditorPane.

The content of the pane is not loaded from a resource but dynamically created and available as a String.

How can I get my JEditorPane to scroll to the proper location? (in the example it should scroll to the top) The listener only catches null, which adds to the problem.

Here's my SSCCCE:

public static void main(final String[] args) {
    final JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    f.setTitle("JEditorPane Test");

    final String text = "<html><body><a name='link1'>test</a>some text<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />some more text<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />some more text<a href='#link1'>jump to top</a></body></html>";

    final JEditorPane ep = new JEditorPane();
    ep.setContentType("text/html");
    ep.setText(text);
    ep.setEditable(false);
    ep.addHyperlinkListener(new HyperlinkListener() {
        @Override public void hyperlinkUpdate(final HyperlinkEvent pE) {
            if (HyperlinkEvent.EventType.ACTIVATED == pE.getEventType())
                System.out.println("ep link click: " + pE.getURL());
        }
    });

    final JScrollPane sp = new JScrollPane(ep);
    f.add(sp);

    f.setBounds(200, 200, 400, 400);
    f.setVisible(true);
}
Choong answered 27/12, 2012 at 12:20 Comment(0)
C
9

Okay I finally got this solved.

I had been testing around with scrollToReference(), but it somehow didn't work. Then I played with HTML parsing and anchors and carets and setCaretPosition() which worked only sometimes. Then out of pure coincidence I had included scrollToReference() again in my code and scrolling suddenly worked... and still works flawlessly!

Here's the working code:

public static void main(final String[] args) {
    final JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    f.setTitle("JEditorPane Test");

    final String text = "<html><body><a name='link1'>test</a>some text<br /><a href='#thisisbottom'>down</a><br /><br /><br /><br /><br /><br /><br /><br /><br /><a name='mid1'></a>some more text<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />some more text [<a href='#link1'>jump to top</a>] <br /> or jump to <a name='thisisbottom' href='#mid1'>center</a></body></html>";

    final JEditorPane ep = new JEditorPane();
    ep.setContentType("text/html");
    ep.setText(text);
    ep.setEditable(false);
    ep.addHyperlinkListener(new HyperlinkListener() {
        @Override public void hyperlinkUpdate(final HyperlinkEvent pE) {
            if (HyperlinkEvent.EventType.ACTIVATED == pE.getEventType()) {
                System.out.println("JEditorPane link click: url='" + pE.getURL() + "' description='" + pE.getDescription() + "'");
                String reference = pE.getDescription();
                if (reference != null && reference.startsWith("#")) { // link must start with # to be internal reference
                    reference = reference.substring(1);
                    ep.scrollToReference(reference);
                }
            }
        }
    });

    final JScrollPane sp = new JScrollPane(ep);
    f.add(sp);

    f.setBounds(200, 200, 400, 400);
    f.setVisible(true);
}
Choong answered 2/1, 2013 at 12:23 Comment(6)
Oh yeah, it's not obvious, but the JEditorPane takes control of its sorrounding JScrollPane for scrolling.Choong
BTW is there any way to mark this as solved? Other than manually editing the title?Choong
Yes, select your own answer as accepted (or the answer that solve the question), this is how it's marked as solved in StackOverflow.Illbred
Quick remark from my side: Looks like the old code for some reason did not have the scrolling code included anymore. I now updated the answer with multiple links and scrolling.Choong
I had an issue with the cursor not reverting back from the hand after clicking the link. Adding the line setCursor(editorKit.getDefaultCursor()); fixed it.Protection
Did you put it right after ep.scrollToReference(reference);? What OS+JVM are you using? Where does editorKit come from? So I can update the example with comments accordingly, if this fixes problems.Choong
C
1

You can do that this way:

import java.awt.Point;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

public class PaneWithScroll {
  private static final String TEXT = 
      "<html>"
        + "<head>"
        + "</head>"
        + "<body>"
          + "<p><a href=\"#top\">Go top</a></p>"
        + "</body>"
      + "</html>";
  private static final String TOP = "#top";

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        JFrame frame = new PaneWithScrollFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
      }
    });
  }

  private static class PaneWithScrollFrame extends JFrame {
    private PaneWithScrollFrame() {
      super.setName("Pane With Scroll");
      this.addComponents();
      super.setSize(640, 480);
    }

    private void addComponents() {
      JEditorPane editorPane = new JEditorPane();
      editorPane.setContentType("text/html");
      editorPane.setEditable(false);
      editorPane.setText(TEXT);

      final JScrollPane scrollpane = new JScrollPane(editorPane);

      editorPane.addHyperlinkListener(new HyperlinkListener() {
        @Override
        public void hyperlinkUpdate(HyperlinkEvent e) {
          if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
            String description = e.getDescription();
            if (TOP.equals(description)) {
              JViewport viewport = scrollpane.getViewport();
              viewport.setViewPosition(new Point(0, 0));
            }
          }
        }
      });

      super.add(scrollpane);
    }
  }
}
Casavant answered 27/12, 2012 at 12:46 Comment(2)
Thank you very much for this input, it already helped a little. BUT I was looking for a more general solution. Thanx to your post I actually found one, will post it below.Choong
Not asked to go top but to any anchor linkDoggone

© 2022 - 2024 — McMap. All rights reserved.