Learn how to add hyperlink enter/exit events to the Swing HTMLEditorKit If you’ve played around with the Swing component set at all, you have undoubtedly seen the JEditorPane class displaying some pretty spiffy HTML documents. JEditorPane doesn’t render HTML all by itself, though. It uses a group of classes that make up what Swing refers to as an editor kit. The editor kit attached to a JEditorPane tells the pane how to draw text, images, and components based on a stream of input data — like an HTML file. It also dictates how the pane will react to user input. For example, when displaying an HTML document, JEditorPane uses the HTMLEditorKit class.The HTMLEditorKit class delegates parsing and rendering responsibilities to a group of HTML-aware classes. The event handling is done directly in HTMLEditorKit itself. The editor kit has a mouse listener, and when mouse clicks occur, HyperlinkEvent objects are generated. This means that not only do images show up, but the hyperlinks actually work! Well, almost. The HyperlinkEvent class appears to support three subcategories of hyperlink events — entered, exited, and activated — so, you should be able to write a pretty quick listener class that does all the work for you: changing the mouse pointer when it passes over a link and jumping to a new document when the user clicks on the link. SimpleLinkListener is just such a class. (Of course, this version also has a few extras built in to allow you to track current and future sites, just as you’ve come to expect from a real browser.)With the listener attached to an editor pane, you’ll have a minimal browser functioning in less than 30 lines of code. The TestBrowser class shows a slightly more interesting browser (see below) written with only 75 lines of code. Not bad, eh?If you want to take this app out for a quick test drive, just click on the button below. Of course, you’ll need to have Swing installed on your system. See the sidebar, Installing Swing, for step-by-step instructions on the installation process. One thing to watch out for, though. The HTML 3.2 support in the HTMLEditorKit is not quite perfect yet. While this area is under constant improvement, you may find some HTML pages which throw errors during formatting. (They throw a javax.swing.text.StateInvariantError to be precise.) It shouldn’t stop the page from loading, but you might not see everything exactly where the authors wanted it go. Most basic HTML pages do not display these errors.You need a Java-enabled browser to see this applet.Now, this little app could easily serve as a help system or in-lined browser, but there’s a small problem, which you’ve likely already noticed: although jumping to links works great, the mouse cursor never changes as you move it over links. In addition, the status bar is never updated with the preview URL. In fact, the enter and exit link events aren’t generated at all. I’m not really sure why this happens; I suppose it could be an oversight in the design. At any rate, it’s frustrating and it needs to be fixed.From the beginning, the Swing component designers made an effort to create flexible and extensible classes. You’ll take advantage of this flexibility to develop your patch. As discussed above, JEditorPane makes it easy to display various different types of styled text (red text, bold text, or bold red text, for example) using editor kits like the HTMLEditorKit. For displaying HTML, you often can rely on that editor kit, which is located in javax.swing.text.html.HTMLEditorKit. The documentation maintains that this piece is easily replaced with your own editor kit. While easy is always a relative term, in this case it really is easy. It’s easy because you get to build on what’s already there — you don’t have to start from scratch.If you look through the HTMLEditorKit source code, you’ll quickly locate the protected method that generates the hyperlink activation events, activateLink(). If you continue to poke around, you’ll find that the mouseClicked() method calls activateLink() to respond to a mouse click. If the click happens over a valid hyperlink, an event is fired.This is precisely your problem area. You need to modify this class to react to mouse motion events as well. You can extend HTMLEditorKit and make those modifications in four steps: Step 1. Overwrite the install() method so the default listener (which doesn’t generate all the events you need) isn’t attached to the editor pane. You’ll attach your own listener.Step 2. Create your listener by modifying the listener from the old HTMLEditorKit to watch for all mouse events.Step 3. Teach the activateLink() method to distinguish between enter/exit events and activation events. Step 4. Make sure the editor pane uses your new editor kit.The new HTMLEditorKit classPatchedHTMLEditorKit contains the code for Steps 1 through 3. Step 1 is the install() method.This method is pretty straightforward; it simply sets up the customized listener: public void install(JEditorPane c) { c.addMouseListener(myController); c.addMouseMotionListener(myController); } When you start developing your own editor kits, you’ll use this method to attach your own plug-ins to the editor pane.The code for Steps 2 and 3 is tucked away in the LinkController inner class. Rather than extending the MouseAdapter class, extend the Swing (1.1, not 1.0.x) MouseInputAdapter class, which will give you all the various mouse events, including mouse motion events. Mimic the mouseClicked() method in the mouseMoved() method. Both methods will then end up calling the activateLink() method and that’s where the real work happens. So much for Step 2.Step 3 is a little more interesting. Consider what you need to keep track of in order to make the mouse cursor change properly. You can’t just generate new events every time the mouse moves. You only want events when you enter or exit an area on the screen occupied by a link. Even when you’re over a link, you only fire events coming in and going out of that area. (Technically, continuously firing enter events doesn’t harm anything, but it is terribly inefficient.) To handle that small problem, you need to store a reference to the current link you’re over. That way, you know when you’re inside a link for which you’ve already generated an enter event. You can also use that reference as an alert that you need to generate an exit event. Given a mouse location, you can use the getCharacterElement() method to determine if you’re on a link. If you’re over a valid link, you need to determine whether you’re jumping or not. If you’re jumping, then go ahead and generate an ACTIVATED event (and signal the need to generate an EXITED event as well). If you’re just moving the mouse, you’ll have to decide on one of two scenarios:You’re over a valid link that you haven’t been on before, so fire an ENTERED event.You’re over a valid link that you have been on before, so do nothing.If it turns out that you’re not over a link at all, check to see if you were over one last time around. If so, signal the need to generate an EXITED event.After all that checking, you still need to run one more test. If you (the user) moved out of a valid link or made a jump, fire the EXITED event. Last, tell the JEditorPane class to use your editor kit for text/html-type documents:JEditorPane jep = new JEditorPane(); jep.setEditorKitForContentType("text/html", new PatchedHTMLEditorKit()); Just in case you were wondering, the extension and mime types are stored in the content-types.properties file in the lib directory under your JDK installation. (While the text/html type doesn’t require an update, you could certainly use this file to add your own types.)ConclusionOne of the great benefits of this process is that you have now successfully installed your own customized editor kit (or at least a slightly modified editor kit). You could further tweak the PatchedHTMLEditorKit class to report different events, make links change color when you’re over them, react to your own custom URLs, activate local programs, or any number of related tasks. In short, you really can plug in your own editor kits without much trouble. And you don’t need to apologize for your demo anymore! Marc Loy is a senior programmer at Galileo Systems, LLC, and coauthor of Java Swing (O’Reilly, 1998). During the daylight hours, Marc teaches Java and Perl to various companies, including Sun Microsystems. He has worked and played with Java since the alpha days and can’t find his way back to C. Marc is currently developing an interactive learning application at Galileo written entirely in Java. JavaSoftware DevelopmentHTML