This adds basic support (<a> tag) for HTML hyperlinks, including the
mouse-hover cursor change.

2006-11-02  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JEditorPane.java
        (setContentType): Strip off attributes.
        * javax/swing/text/html/HTMLEditorKit.java
        (LinkController.activateLink(int,JEditorPane,int,int): New
        method. Implements activation of a hyperlink.
        (LinkController.activateLinke(int,JEditorPane)): Delegate
        to the other activateLink() method.
        (LinkController.createHyperlinkEvent): New helper method.
        (LinkController.mouseClicked): Implemented to activate the link.
        (LinkController.mouseDragged): Added comment that this
        method does nothing.
        (LinkController.mouseMoved): Update cursor for hyperlinks.
        (mouseHandler): Renamed field to linkController.
        (HTMLEditorKit): Create a link controller.
        (clone): Give the clone a new link controller.
        (deinstall): De-install link controller as mouseMotionListener too.
        (install): Install link controller as mouseMotionListener too.

/Roman

Index: javax/swing/JEditorPane.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JEditorPane.java,v
retrieving revision 1.37
diff -u -1 -5 -r1.37 JEditorPane.java
--- javax/swing/JEditorPane.java	18 Oct 2006 15:17:06 -0000	1.37
+++ javax/swing/JEditorPane.java	2 Nov 2006 13:56:22 -0000
@@ -857,30 +857,37 @@
     // TODO: Implement this properly.
     super.replaceSelection(content);
   }
 
   /**
    * Scrolls the view to the given reference location (that is, the value
    * returned by the UL.getRef method for the URL being displayed).
    */
   public void scrollToReference(String reference)
   {
     // TODO: Implement this properly.
   }
 
   public final void setContentType(String type)
   {
+    // Strip off content type parameters.
+    int paramIndex = type.indexOf(';');
+    if (paramIndex > -1)
+      {
+        // TODO: Handle character encoding.
+        type = type.substring(0, paramIndex).trim();
+      }
     if (editorKit != null
 	&& editorKit.getContentType().equals(type))
       return;
     	      
     EditorKit kit = getEditorKitForContentType(type);
 	    	
     if (kit != null)
       setEditorKit(kit);
   }
 
   public void setEditorKit(EditorKit newValue)
   {
     if (editorKit == newValue)
       return;
     	
Index: javax/swing/text/html/HTMLEditorKit.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/HTMLEditorKit.java,v
retrieving revision 1.35
diff -u -1 -5 -r1.35 HTMLEditorKit.java
--- javax/swing/text/html/HTMLEditorKit.java	31 Oct 2006 11:57:10 -0000	1.35
+++ javax/swing/text/html/HTMLEditorKit.java	2 Nov 2006 13:56:22 -0000
@@ -34,44 +34,50 @@
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.text.html;
 
 
 import gnu.classpath.NotImplementedException;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseMotionListener;
 import java.awt.Cursor;
+import java.awt.Point;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.Serializable;
 import java.io.StringReader;
 import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
 
 import javax.accessibility.Accessible;
 import javax.accessibility.AccessibleContext;
 
 import javax.swing.Action;
 import javax.swing.JEditorPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.AttributeSet;
 import javax.swing.text.BadLocationException;
 import javax.swing.text.Document;
 import javax.swing.text.EditorKit;
 import javax.swing.text.Element;
 import javax.swing.text.MutableAttributeSet;
 import javax.swing.text.StyleConstants;
 import javax.swing.text.StyledDocument;
 import javax.swing.text.StyledEditorKit;
 import javax.swing.text.TextAction;
 import javax.swing.text.View;
 import javax.swing.text.ViewFactory;
 import javax.swing.text.html.parser.ParserDelegator;
 
 /* Move these imports here after javax.swing.text.html to make it compile
    with jikes.  */
@@ -100,81 +106,162 @@
        */
       public LinkController() 
       {
         super();
       }
       
       /**
        * Dispatched when the mouse is clicked. If the component
        * is read-only, then the clicked event is used to drive an
        * attempt to follow the reference specified by a link
        * 
        * @param e - the mouse event
        */
       public void mouseClicked(MouseEvent e)
       {
-        /*
-         These MouseInputAdapter methods generate mouse appropriate events around
-         hyperlinks (entering, exiting, and activating).
-         */
-        // FIXME: Not implemented.
+        JEditorPane editor = (JEditorPane) e.getSource();
+        if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
+          {
+            Point loc = e.getPoint();
+            int pos = editor.viewToModel(loc);
+            if (pos >= 0)
+              activateLink(pos, editor, e.getX(), e.getY());
+          }
       }
       
       /**
        * Dispatched when the mouse is dragged on a component.
        * 
        * @param e - the mouse event.
        */
       public void mouseDragged(MouseEvent e)
       {
-        /*
-        These MouseInputAdapter methods generate mouse appropriate events around
-        hyperlinks (entering, exiting, and activating).
-        */
-        // FIXME: Not implemented.     
+        // Nothing to do here.
       }
       
       /**
        * Dispatched when the mouse cursor has moved into the component.
        * 
        * @param e - the mouse event.
        */
       public void mouseMoved(MouseEvent e)
       {
-        /*
-        These MouseInputAdapter methods generate mouse appropriate events around
-        hyperlinks (entering, exiting, and activating).
-        */
-        // FIXME: Not implemented.
+        JEditorPane editor = (JEditorPane) e.getSource();
+        HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+        if (! editor.isEditable())
+          {
+            Document doc = editor.getDocument();
+            if (doc instanceof HTMLDocument)
+              {
+                Cursor newCursor = kit.getDefaultCursor();
+                HTMLDocument htmlDoc = (HTMLDocument) doc;
+                Point loc = e.getPoint();
+                int pos = editor.viewToModel(loc);
+                Element el = htmlDoc.getCharacterElement(pos);
+                if (pos < el.getStartOffset() || pos >= el.getEndOffset())
+                  el = null;
+                if (el != null)
+                  {
+                    AttributeSet aAtts = (AttributeSet)
+                                   el.getAttributes().getAttribute(HTML.Tag.A);
+                    if (aAtts != null)
+                      newCursor = kit.getLinkCursor();
+                  }
+                if (editor.getCursor() != newCursor)
+                  editor.setCursor(newCursor);
+              }
+          }
       }
-      
+
       /**
        * If the given position represents a link, then linkActivated is called
-       * on the JEditorPane. Implemented to forward to the method with the same
-       * name, but pos == editor == -1.
-       * 
-       * @param pos - the position
-       * @param editor - the editor pane
+       * on the JEditorPane.
+       *
+       * @param pos the position
+       * @param editor the editor pane
        */
-      protected void activateLink(int pos,
-                                  JEditorPane editor)
+      protected void activateLink(int pos, JEditorPane editor)
       {
-        /*
-          This method creates and fires a HyperlinkEvent if the document is an
-          instance of HTMLDocument and the href tag of the link is not null.
-         */
-        // FIXME: Not implemented.
+        activateLink(pos, editor);
+      }
+
+      private void activateLink(int pos, JEditorPane editor, int x, int y)
+      {
+        // TODO: This is here for future extension for mapped links support.
+        // For the time beeing we implement simple hyperlinks.
+        Document doc = editor.getDocument();
+        if (doc instanceof HTMLDocument)
+          {
+            HTMLDocument htmlDoc = (HTMLDocument) doc;
+            Element el = htmlDoc.getCharacterElement(pos);
+            AttributeSet atts = el.getAttributes();
+            AttributeSet anchorAtts =
+              (AttributeSet) atts.getAttribute(HTML.Tag.A);
+            String href = null;
+            if (anchorAtts != null)
+              {
+                href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
+              }
+            else
+              {
+                // TODO: Implement link maps here.
+              }
+            HyperlinkEvent event = null;
+            if (href != null)
+              event = createHyperlinkEvent(editor, htmlDoc, href,
+                                           anchorAtts, el);
+            if (event != null)
+              editor.fireHyperlinkUpdate(event);
+          }
+        
+      }
+
+      /**
+       * Creates a HyperlinkEvent for the specified href and anchor if
+       * possible. If for some reason this won't work, return null.
+       *
+       * @param editor the editor
+       * @param doc the document
+       * @param href the href link
+       * @param anchor the anchor
+       * @param el the element
+       *
+       * @return the hyperlink event, or <code>null</code> if we couldn't
+       *         create one
+       */
+      private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
+                                                  HTMLDocument doc,
+                                                  String href,
+                                                  AttributeSet anchor,
+                                                  Element el)
+      {
+        URL url;
+        try
+          {
+            URL base = doc.getBase();
+            url = new URL(base, href);
+            
+          }
+        catch (MalformedURLException ex)
+          {
+            url = null;
+          }
+        // TODO: Handle frame documents and target here.
+        HyperlinkEvent ev =
+          new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, url,
+                             href, el);
+        return ev;
       }
     }
   
   /**
    * This class is used to insert a string of HTML into an existing
    * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
    * identifies the parent in the document to add the elements to. The second, (addTag), 
    * identifies that the first tag should be added to the document as seen in the string.
    * The parser will generate all appropriate (opening/closing tags_ even if they are not
    * in the HTML string passed in.
    */
   public static class InsertHTMLTextAction
     extends HTMLTextAction
     {
       
@@ -819,47 +906,47 @@
   Cursor linkCursor;
   
   /**
    * The default cursor.
    */
   Cursor defaultCursor;
   
   /**
    * The parser.
    */
   Parser parser;
   
   /**
    * The mouse listener used for links.
    */
-  LinkController mouseListener;
+  private LinkController linkController;
   
   /** The content type */
   String contentType = "text/html";
   
   /** The input attributes defined by default.css */
   MutableAttributeSet inputAttributes;
   
   /** The editor pane used. */
   JEditorPane editorPane;
     
   /**
    * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
    */
   public HTMLEditorKit()
   {
-    // Nothing to do here.
+    linkController = new LinkController();
   }
   
   /**
    * Gets a factory suitable for producing views of any 
    * models that are produced by this kit.
    * 
    * @return the view factory suitable for producing views.
    */
   public ViewFactory getViewFactory()
   {
     if (viewFactory == null)
       viewFactory = new HTMLFactory();
     return viewFactory;
   }
   
@@ -1004,74 +1091,75 @@
    * @returns the content type supported.
    */
   public String getContentType()
   {
     return contentType;
   } 
   
   /**
    * Creates a copy of the editor kit.
    * 
    * @return a copy of this.
    */
   public Object clone()
   {
     // FIXME: Need to clone all fields
-    return (HTMLEditorKit) super.clone();
+    HTMLEditorKit copy = (HTMLEditorKit) super.clone();
+    copy.linkController = new LinkController();
+    return copy;
   }
   
   /**
    * Copies the key/values in elements AttributeSet into set. 
    * This does not copy component, icon, or element names attributes.
    * This is called anytime the caret moves over a different location. 
    * 
    * @param element - the element to create the input attributes for.
    * @param set - the set to copy the values into.
    */
   protected void createInputAttributes(Element element,
                                        MutableAttributeSet set)
   {
     set.removeAttributes(set);
     set.addAttributes(element.getAttributes());
     // FIXME: Not fully implemented.
   }
   
   /**
    * Called when this is installed into the JEditorPane.
    * 
    * @param c - the JEditorPane installed into.
    */
   public void install(JEditorPane c)
   {
     super.install(c);
-    mouseListener = new LinkController();
-    c.addMouseListener(mouseListener);
+    c.addMouseListener(linkController);
+    c.addMouseMotionListener(linkController);
     editorPane = c;
-    // FIXME: need to set up hyperlinklistener object
   }
   
   /**
    * Called when the this is removed from the JEditorPane.
    * It unregisters any listeners.
    * 
    * @param c - the JEditorPane being removed from.
    */
   public void deinstall(JEditorPane c)
   {
     super.deinstall(c);
-    c.removeMouseListener(mouseListener);
-    mouseListener = null;
+    c.removeMouseListener(linkController);
+    c.removeMouseMotionListener(linkController);
     editorPane = null;
   }
   
   /**
    * Gets the AccessibleContext associated with this.
    * 
    * @return the AccessibleContext for this.
    */
   public AccessibleContext getAccessibleContext()
   {
     // FIXME: Should return an instance of 
     // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
     // Not implemented yet.
     return null;
   }

Reply via email to